競技プログラミング練習会
2019 Normal
第1回 プログラミングの基礎講座
担当:zeke
プログラミング基礎
プログラミング基礎
- プログラミング経験がない人~少しある人向けに、競プロでよく使う知識を教えます。
- 競プロでは、C++という言語が主流でおすすめなので、C++を例にとって話します。
C++とは
- プログラミング言語の1つ。
- 実行速度が速く、実行時間制限のある競プロに向いている。
- 標準ライブラリ(標準で使える関数の詰め合わせ)に含まれる関数が豊富。
- 競プロerに使ってる人が多いので、先輩やGoogle先生に聞きやすい。
基本的なこと
コメント
- 人間用のメモで実行されない部分のこと。
- // の後はその行では全部コメント
- 複数行にコメントが渡る場合は、/* */の間に挟むと良い。
// ここからコメント
/* コメント
コメント
コメント */
文
- プログラミングにおいて、;までの1節を文と呼ぶ。
- 今は分からなくても、今から例がいっぱい出てくるので、何となく分かるようになると思う。
int a; //これで1文
a = 5; //これも文
形式
- 普通はこう書くよっていうのを説明します。
- 1文ごとに改行する。
- 基本的に{}の中に1つ入るごとに[Tabキー]を押してコードを右にずらし、{}から出るごとに1つ左に戻す。
- [Tab]1回分のスペースをインデントと呼ぶ。
- 多くのエディタはインデントを勝手にやってくれる。
//コードの中身は後でやるので、インデントに注目。
#include <bits/stdc++.h>
using namespace std;
int main(){
int a = 1; //インデント1つ
if(a>0){ //インデント1つ
a++; //インデント2つ
if(a>2){ //インデント2つ
a++; //インデント3つ
} //インデント2つ
} //インデント1つ
}
エラーについて
- エラーは自分のコードのどこが間違っているかを教えてくれるので、活用しよう。
- エラーの種類
-
コンパイルエラー
- 文法の誤り
-
実行時エラー
- 文法以外の計算中に発生する致命的な誤り
-
論理エラー
- 普通に実行されるが、期待される値が出ない。
-
コンパイルエラー
コンパイルエラー"CE"
- 自分の書いたコードを機械語に置き換える(翻訳する)ことをコンパイルという。
- コードの文法が誤っていてコンパイルできないときにこのエラーが発生する。
- 特徴:そもそも実行されない。
- 具体的には
- 文末の';'忘れ
- (){}の対応が合っていない
- 全角スペース(コメントなどでしか使えない)
- 意味の通らないタイプミス
実行時エラー"RE"
- 文法に誤りはなく、コンパイルはできるが、実行中に致命的なエラーが発生し、それ以上実行できなくなったことを示すエラー。
- 特徴:エラーが発生する行まで実行される。
- 例えば…
- 0で除算
- 配列外参照(次回でやります)
論理エラー(不正解)"WA"
- 一見何の問題もなく実行されるが、期待された値が出力されない。
- 例えば…
- +と-を間違えてタイプ
- タイプミス?
- 勘違い?
- そもそも解法が嘘
- +と-を間違えてタイプ
- 一部のケースでは正しく動く(ように見える)こともあり、発見が困難。
基本演算
基本的な演算
- 変数宣言
- 代入
- 算術演算
- 比較演算
- 論理演算
- 代入演算
変数とは
- 変数とは、数字の代わりに値を入れておく文字列のこと。
- 変数は、複数回同じ数字を使って処理するときなどに、わかりやすくするために使うことが多い。
型とは
- データの種類のこと。
- 以下のような型がよく用いられる。
- int型(整数型)
- double型(小数型)
- string型(文字列型)
変数宣言
- 変数宣言とは、変数をこれから使用すると明記すること。
- この時に、変数の型(変数の性質)を決める。
- これ以降、最も基本的な型であるint型について話す。
int value; //変数valueをint型で宣言
double y; //変数yをdouble型で宣言
string str; //変数strをstring型で宣言
int a,b; //同じ型であれば複数の変数宣言を','でまとめられます
変数名に関する注意
- 変数名には条件があり、なんでも良いというわけではない。
- 利用できない変数名
- 数字から始まるもの。
- 他の変数名と重複しているもの。
- C++の内部で既に用いられている名前(int,doubleなど)を用いているもの。
- これらを守らないと、コンパイルエラーが起きる。
代入
- "="の記号により代入を示す。
- "="の左の変数に右の値(変数でも良い)を入れる。
int x,y;//変数x,yを整数型で宣言
x = 1;//変数xに1を代入する
y = x;//変数yに変数xの中身である1を代入する
初期化
- 変数の宣言と代入は同時に行うことができる。
- これを「変数を初期化する」という。
int x = 5; //xを5で初期化
int answer = -8; //answerを-8で初期化
int y = x; //変数で初期化することもできます
int a = 2,b = 3; //複数の初期化を','で区切って1文で行えます
算術演算
- 主に四則演算と剰余算のこと。
- "*"を"×"の代りに、"/"を"÷"の代りにそれぞれ使う。
- int型の割り算では、小数部分を切り捨てる。
- "%"をa%b=(aをbで割った時の余り)のように定義。剰余算という。
- 以下では先ほどの代入と組み合わせて使っている。
int x;
x = 1+1;//xに1+1すなわち2を代入
x = 2-1;//xに2-1すなわち1を代入
x = 3*2;//xに3×2すなわち6を代入
x = 4/2;//xに4÷2すなわち2を代入
x = 4/3;//一般には4/3=1.33...だが、
//int型の除算では小数が破棄されるので、4/3=1が代入される
x = 10%3;//xに10÷3の余りである1を代入
算術演算の順序
- 通常の計算と同様で、原則左から計算され、"*","/","%"の計算が優先される。
- "(",")"によって他の演算に対し優先的に計算することもできる。
int x;
x = 1+1*2; //xに1+1*2すなわち3を代入
x = 2*1+4*3; //xに2+12=14を代入
x = 2*(1+4)*3; //xに2*5*3すなわち30を代入
x = (2+3)*1+2/2;//xに5+1すなわち6を代入
x = 1+3%3; //xに1+(3%3)=1+0=1を代入
x = x + 1; //xにx+1=2を代入
論理値と比較演算
- C++では、論理的に真であることを"1(or0以外)",偽であることを"0"として内部で処理しています。
- この真偽を示す1,0のことを論理値などと呼ぶ。
- 比較演算とは、その比較が正しいと1(真)、誤りだと0(偽)を返す演算のこと。
- 例えば、1<2というのは真なので値として1を返します。
比較演算
- 比較演算に使われる演算子は"<",">","<=",">=", "==","!="の6つあります。
- 最初の2つはそのまま。
- "<=",">="はそれぞれ"≤","≥"を示す。
- "=="は左辺と右辺が等しい時に真となる。("="は代入なので注意)
- "!="は左辺と右辺が等しくない時に真となる。
- つまり、"!="は"≠"を示す。
1<2; //真なので1を示す。
2<1; //偽なので0を示す。
2<=3; //真なので1を示す。
2==3; //偽なので0を示す。
//2=3; //コンパイルエラー
2!=3; //真なので1を示す。
int x = 1;
x>0; //真なので1を示す。
論理演算
- 論理演算とは、比較演算による真偽の値を計算する演算。
- 「かつ」、「または」、「否定」の3種類があります。
- 次のページで具体的な説明をします。
論理演算
- 論理演算子として使われるものには"&&","||","!"の3つあります。
- "&&"は「かつ」を表し、真と真を演算した時のみ真を返し、他の場合は偽を返す。
- "||"は「または」を表し、どちらかが真であれば真を返し、共に偽であれば偽を返す。
- "!"は「否定」を表し、直後の論理式が真であれば偽、偽であれば真を返します。
1<2&&2<3;//真かつ真なので真(1)を示す。
2<1&&2<3;//真かつ偽なので偽(0)を示す。
2<1||2<3;//真または偽なので真(1)を示す。
(1<2&&2<3)||(2<1&&2<3);//括弧の中はそれぞれ真、偽を示す。故に真または偽なので真(1)。
!(2<3);//真を否定しているので偽(0)を示す。
代入演算について
- 今の値を2増やしたい時( "x=x+2;")、少し不便。
- 実はこれを簡単な式("x+=2;")にできる。
- 単純な代入と併せて、これらを代入演算という。
代入演算
- 例えば、x=x+2;であれば、x+=2; のようにできる。
- 要は、算術演算子に'='をつけるだけ。
int x = 4,y = 5;
x += 3; //x=x+3; なので x=7
x -= 3; //x=x-3; なので x=4
x *= 2; //x=x*2; なので x=8
x /= 4; //x=x/4; なので x=2
x %= 2; //x=x%2; なので x=0
x += y+1; //右辺に変数を入れたり、複数の項を入れることもできます
インクリメント,デクリメント
- プログラミングにおいて、しばしば1増やしたり、1減らしたりする処理が行われる。
- 毎回 x+=1; や x-=1; のように書くのは面倒。
- ++x;やx++;と書くとx+=1;という処理と(大体)同じになる。
- --x;やx--;と書くとx-=1;という処理と(大体)同じになる。
- 増やす方を「インクリメント」、減らす方を「デクリメント」と呼ぶ。
インクリメント,デクリメントのおまけ
- インクリメント:x++;や++x;のことで、x+=1;と同じ。
- デクリメント:x--;や--x;のことで、x-=1;と同じ。
- ++,--は左右どちらにつけてもいい。
- (厳密には異なるが割愛)
int x = 0;
++x; //x+=1; なので x=1
x++; //x+=1; なので x=2
--x; //x-=1; なので x=1
x--; //x-=1; なので x=0
標準入出力
標準入力と標準出力について
- 競プロでは、問題が抽象的で、採点のために、無作為(或いは間違いやすそう)な入力が与えられる。
- この入力は(実行引数でなく、)「標準入力」によって与えられる。
- そして、「標準出力」によって回答する。
例題
- 値として整数a,bが与えられる。
- a+bを計算し、出力しなさい。
- -1000≤a, b≤1000
入力例
出力例
3 5
8
例題を解くために
- a+bを求めるだけなので解くのは簡単そう。
- 競プロでは、入力が複数個与えられ(今回の場合はa,bの値)、それら全てに正しく答えられれば、正解(AC)と見なされる。
- では、実際どうやってデータを受け取り、どうやって答えようか?
標準入力
- 一般的な入力のこと。
- これにより、外部からデータを受け取れる。
- 今回はcinというのをやります。
cin
- cinでは、int型の場合なら、整数1つ(連続した数字)を受け取る。"cin>>x;"で実行。
- つまり、空白,改行で区切られた整数を、自動で前から変数に入れてくれる。
int x;
cin >> x;//このようにcinの後に">>"を書き、その後に「入力された値」を入れる変数を書く。
//元々xに値が入っている場合でも、書き換えられる。
//">>"はcinからxに値が入っていくイメージ
int y,z;
cin >> y >> z;//連続して入力する場合はcin>>y;cin>>z;と書かなくてもこのように書ける。
下の例の場合はcin>>x>>y>>z;で
の入力により、x=311,y=51,z=2となる。
311
51
2
311 51 2
標準出力
- 一般的な出力のこと。
- これによって値を外部(画面など)に出し、回答できる。
- 今回はcoutをやります。
- 回答の最後には必ず改行しましょう。
cout
- coutでは、cout<<x;によって出力できる。(cinと向きが逆)
- 改行はcout<<endl;でできる。(End of Line)
- 空白、文字は" "で挟んで出力しよう。
int x = 1;
cout << x; //coutの後に"<<"を書き、その後に出力する変数名を書く。
//"<<"はxからcoutに入れるイメージ
cout << endl; //endlは行の終了を意味する。これを出力すると改行される。
cout << x << endl; //連続して出力する場合はcin同様にまとめて書ける。
int y = 2;
cout << x+y << endl; //演算後の値を出力できる。
cout << x << " " << y << endl; //""で挟むことで、半角英数字,記号,空白を出力可能。
cout << "Hello, world!" << endl; //英文ももちろん出力できる。
実際のコード例
テンプレート
- 取り敢えずこれだけ書いて、"/*コード */"の代わりに今までのようなコードを書こう。
- 今日のコンテストをやる分にはこれだけ知っていれば十分。
#include<bits/stdc++.h> //取り敢えず書く
using namespace std; //取り敢えず書く
int main(){ //取り敢えず書く
/* コード */ //今までやったようにコードを書く
}
例題のコード例
#include<bits/stdc++.h> //取り敢えず書く
using namespace std; //取り敢えず書く
int main(){ //取り敢えず書く
int a,b; //変数宣言
cin >> a >> b; //入力
cout << a+b << endl; //出力
}
- 特に難しいところはないと思う。
- この書き方をマスターしよう。
条件分岐文if
条件分岐文if
- 先ほど行った、比較演算,論理演算の結果に応じて、{}内の文を選択的に実行する構文。
- if(条件){文}で構成される。
- ()内が真(0以外)の時のみ実行される。
int x = 3;
int y = 4;
if(x < y&&x <= 3){ //x<yかつx<=3が真の時のみ実行される。
//今回は実行される。
x++;
y += x;
}
if(1){ //極端な話、これでも(ifの意味はないが)実行される。
y++;
}
else
- 手前のifの条件を満たさないときに、そのifの直後のelseの{}内が実行される。
- 下記のような書き方("}else{")をする事が多い。
- if文なしでは使えない。
int x = 5;
if(x < 4){ //今回は実行されない。
x++;
}else{ //こっちが実行される。if(x>=4)と同じ。
x--;
}
else if
- 手前のifの条件を満たさないときに、そのifの直後のelse ifの条件が満たされていれば、実行される。
- if文なしでは使えない。
int x = 5;
if(x < 4){ //今回は実行されない。
x++;
}else if(x < 6){ //上のif文で弾かれた時のみ、()内の判断が行われる。
//よって、この時の条件は4≤x<6である。こっちが実行される。
x--;
}
複数の場合分け
- if→else if→...→else if→elseのように分けることで、複数の場合分けに対応できる。
int x = 5;
if(x < 3){ //実行されない。
x = 1;
}else if(x < 5){ //条件は3≤x<5であり、実行されない。
x = 2;
}else if(x < 7){ //条件は5≤x<7であり、実行される。
x = 3;
}else{ //条件は7≤x であり、実行されない。
x = 4;
}
繰り返し文for,while
繰り返し文
- 同じ文を何回も書くのは面倒。
- 繰り返されることが分かっているなら、後述する繰り返し文を使ってまとめられる。
- 今回はwhileとforの2種類を紹介する。
繰り返し文while
- while(条件){文}
- ()内の条件が満たされている間、{}内が繰り返され続ける。
- 各ループの最初に条件の確認をする。
- 終了時には条件を満たさないようにしないと、無限にループしてしまうので注意(兵庫県警に捕まります)
繰り返し文while
- ()内の条件が満たされている間、{}内が繰り返され続ける。
int x = 5,a = 1;
while(x > 0){ //2^xを求める。
//x=5から1ずつ減っているので、今回は5周する。
a *= 2;
x--; //最後には条件を満たさないようにしないと永遠ループする羽目に
}
繰り返し文for
- for(①;②;③){文}
- ①には変数の初期化
- ②には繰り返す条件
- ③には変数の値を更新する文
- これらをそれぞれ書く
- 2つ目の部分の条件が満たされている間、{}内を繰り返す。
- whileと同じく、各ループの最初に条件を確認する。
- 変数の値の更新は各ループの最後に行われる
繰り返し文for
- for("初期化";"条件";"変数の更新"){文}と書く。
int x = 5,a = 1;
for(int i=0;i<x;++i){ //2^xを求める。
//i=0から1ずつ増えてi<x=5の間ループする。今回は5周する。
a *= 2;
//++i; 各ループの最後に変数の値の更新が行われる。
}
//ここでa=32
int b = 1;
for(;b<32;){ //必ずしも、変数初期化と値の更新はしなくても良い。
b *= 2; //そのため、このようにwhileと同様に書ける。
}
繰り返し文
- for文の方がwhile文よりも複雑だが、その分、無限ループし難いので、初心者にはfor文がオススメ。(頑張って覚えよう)
- もちろん状況によって使い分けてくれると、なお良い。
break,continue文
- break文
- 今入っている中で、一番内側のループを抜ける。
- continue文
- それ以下の処理を飛ばして次のループに移る。
- やや煩雑だが、下に続くif文が楽になる。
int x = 5, a = 1;
while(1){ //2^xを求める
a *= 2;
x--;
if(x<=0){
break; //xが0以下になったら終了
}
}
int x = 10, ans = 0;
//xまでの奇数の2倍と偶数の和を求める。
for(int i=1;i <= x;++i){
if(i%2==1){ //奇数の時
ans += 2*i;
continue;
}
ans += i; //偶数の時
//elseが不要
}
終わり!
お疲れ様でした
コンテストやろう
最後に
オススメの勉強法
- AtCoderというサイトが日本語で競プロができるので、挑戦してみよう。
- まずは、AtCoderのAtCoder Programming Guide for Beginners (APG4b)が、初心者にオススメなので時間あるときに見るといいと思う。
- それが終わったら、AtCoder Beginners Selection を上から解いていこう。
AtCoderのススメ
- 毎週末にコンテストを開いている
- 初心者~上級者向けのコンテストが存在
- コンテストに参加することで、自分の強さを表すレートやランクを表す色が変化する
まずはABCに参加してみよう!
- AtCoder Beginner Contest
- 大体の毎週末に開催される初心者向けコンテスト
- 同時に国内外の約8000人が参加
- 6問の出題
- 1~2問目は簡単なので、今の実力でも解けるかも!?
- 次のABCは です
参考文献
-
プログラミングコンテストチャレンジブック第二版
- 通称 蟻本。
- アルゴリズムのあれこれが載ってます。
- 競プロerのほとんどが持っている。
- 競プロやるなら、買って損はないと思います。
- 部室にも置いてます
-
AtCoder Programming Guide for Beginners(APG4b)
- プログラミングの基本部分についてはかなり参考にしています。
- こっちの方がその部分についてはかなり詳しいので、初心者なら一読の価値あり。

第1回:プログラミング基礎
By kmc_procon2020
第1回:プログラミング基礎
合わせるとスライドが長くなるので分けました
- 549