競技プログラミング練習会

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

繰り返し文

  • 同じ文を何回も書くのは面倒。
  • 繰り返されることが分かっているなら、後述する繰り返し文を使ってまとめられる。
  • 今回はwhileforの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)
    • プログラミングの基本部分についてはかなり参考にしています。
    • こっちの方がその部分についてはかなり詳しいので、初心者なら一読の価値あり。