【競プロ】AtCoderを始めるためのC++の基本

机の上のパソコン プログラミング

競技プログラミング(競プロ)では、計算速度が速いなどの理由からC++が主流です。

AtCoderを始めましたが、C++について勉強したことがなかったのでまとめました。

ほかのプログラミング言語で、基本的なことは理解しているがC++は触ったことがない方向けです。

AtCoderでのC++の基本

AtCoderの「Welcome to AtCoder」問題を解いてみます。

整数a,b,cと、文字列sが与えられます。a+b+cの計算結果と、文字列sを並べて表示しなさい。

AtCoder Welcome to AtCoder

次のプログラムは、C++での解答例です。

//Welcome to AtCoderの解答例
#include<bits/stdc++.h>
using namespace std;

int main () {
  int a, b, c;
  cin >> a >> b >> c;

  string s;
  cin >> s;

  cout << (a + b + c) << " " << s << endl;
  return 0;
}

基本的には、入力があたえられ、それを加工して出力します。

コメント

「//」、「/**/」を使う。

//1行のコメント
//これはコメントになる

//複数行のコメント
/*ここから

コメントになる

ここまで*/

標準ライブラリ

C++には、様々な機能をまとめた標準ライブラリ(STL:Standard Template Library)が用意されています。

そのライブラリを使えるようにするために、次のコードを書きます。

//標準ライブラリのインクルード
#include <bits/stdc++.h>

これを書かなければSTLの機能である「cout」などが使えません。

ちなみに、STLは複数のファイルに分かれて書かれています。「bits/stdc++.h」は、STLのすべて機能を使えるようにするものです。

使いたい機能だけのファイルをインクルードする方法もあります。

Welcome to AtCoder」問題の解答例の場合は、入出力関連の機能の「iostream」をインクルードするだけでも動きます。

//iostreamのインクルード
#include <iostream> 
using namespace std;

int main () {
  int a, b, c;
  cin >> a >> b >> c;

  string s;
  cin >> s;

  cout << (a + b + c) << " " << s << endl;
  return 0;
}

名前空間

STLの機能を使うときには、その機能の前に「std::」を書く必要があります

#include<bits/stdc++.h>

int main () {
  std::cout << "Hello " << std::endl;  //STLの機能を使うために「std::」が必要
  return 0;
}

「std::」を省略するのが、「using namespace std;」です。

これを書くことで、「std::」を省略して書くことができます。

#include<bits/stdc++.h>
using namespace std;

int main () {
  cout << "Hello " << endl;  //「std::」を省略できる
  return 0;
}

入力

cin(シーイン)を使う。「cin >> 変数」で入力を受け取れる。

入力がスペースや改行で区切られていれば、分解して受け取ることもできる。

//入力の受け取り
int a;
cin >> a;


//複数入力の受け取り
int a, b, c;
cin >> a >> b >> c;

出力

cout(シーアウト)を使う。「cout << 出力」で出力する。

「endl」は改行を表す。

//文字列の出力
cout << "Hello, World" << endl;  // => Hello, World


//複数に分けて出力もできる
cout << "Hello" << ", " << "World" << endl;  // => Hello, World


//変数の出力
string s = "Hello, World";
cout << s << endl;  // => Hello, World

文字列

「string 変数名」で宣言する。

文字列sについて

  • s.size() または s.length():文字列の長さの取得
  • s.at(i) または s[i]:i番目の文字。0から始まる
#include<bits/stdc++.h>
using namespace std;

int main () {
  string s = "hello world";

  //文字列の長さ
  cout << s.size() << endl;  // => 11
  cout << s.length() << endl;  // => 11

  //6番目の文字
  cout << s.at(6) << endl;  // => w
  cout << s[6] << endl;  // => w
  return 0;
}

配列

「vector<型> 配列変数名(要素数)」で宣言する。

配列vについて

  • v.size():要素数の取得
  • v.at(i) または v[i]:i番目の要素。0から始まる
#include<bits/stdc++.h>
using namespace std;

int main () {
  vector<string> v;
  v = {"zero", "one", "two", "three", "four", "five"};

  //配列の要素数
  cout << v.size() << endl;  // => 6

  //3番目の要素
  cout << v.at(3) << endl;  // => three
  cout << v[3] << endl;  // => three
  return 0;
}

「型 配列変数名[要素数]」で宣言することも可能。その場合、「v.size()」や「v.at(i)」は使えない。

#include<bits/stdc++.h>
using namespace std;

int main () {
  string v[] = {"zero", "one", "two", "three", "four", "five"};

  //3番目の要素
  cout << v[3] << endl;  // => three
  return 0;
}

2次元配列

「vector<vector<型>> 配列変数名(要素数1, vector<型>(要素数2, 初期値))」で宣言する。

2次元配列vについて

  • v.size():縦の要素数の取得
  • v.at(0).size():横の要素数の取得
  • v.at(i) .at(j):i行目j列目の要素。0から始まる
#include<bits/stdc++.h>
using namespace std;

int main () {
  vector<vector<int>> v(2, vector<int>(3));
  v = {
      {0, 1, 2},
      {3, 4, 5},
  };

  //配列の要素数
  cout << v.size() << endl;  // => 2
  cout << v.at(0).size() << endl;  // => 3

  //1行目2列目の要素
  cout << v.at(1).at(2) << endl;  // => 5 ※2次元配列の要素は0から始まる
  return 0;
}

「型 配列変数名[要素数1][要素数2]」で宣言することも可能。

#include<bits/stdc++.h>
using namespace std;

int main () {
  int v[2][3] = {
      {0, 1, 2},
      {3, 4, 5},
  };

  //1行目1列目の要素
  cout << v[1][1] << endl;  // => 4 ※2次元配列の要素は0から始まる
  return 0;
}

条件分岐

//if-else文
if (a > b) {
  cout << "a > b" << endl;
} else if (a < b) {
  cout << "a < b"<< endl;
} else {
  cout << "a = b" << endl;
}

//三項演算子
cout << (a * b) % 2 == 0 ? "Even" : "Odd") << endl;

繰り返し

//for文
for (int i = 1; i < 5; i++) {
  cout << i << "回目" << endl;
}

//while文
int i = 1;
while (i < 5) {
  cout << i << "回目" << endl;
  i++;
}

breakやcontinueも使える。

//break
for (int i = 1; i < 5; i++) {
  if (i == 3) {
    break;
  }
  cout << i << "回目" << endl;
}

//出力結果
/*
1回目
2回目
*/
//continue
for (int i = 1; i < 5; i++) {
  if (i == 3) {
    continue;
  }
  cout << i << "回目" << endl;
}

//出力結果
/*
1回目
2回目
4回目
*/

配列のすべての要素を出力するときには「for(配列の型 変数名:配列変数名)」で繰り返すこともできる。

vector<int> v;
v = {1, 2, 3, 4, 5};
  
for(int x : v){
	cout << x << endl;
}

//出力結果
/*
1
2
3
4
5
*/

並び替え

「sort(vec.begin(), vec.end())」で配列vecを昇順で並び替える。

#include<bits/stdc++.h>
using namespace std;

int main () {
  vector<int> v = {3, 8, 6, 1, 2, 7, 5, 4};
  
  //配列の並び替え(昇順)
  sort(v.begin(), v.end());
 
  //配列の出力
  for (int i = 0; i < v.size(); i++){
    if (i > 0) {
      cout << " ";
    }
    cout << v[i];  // => 1 2 3 4 5 6 7 8
  }
  
  return 0;
}

配列vecの要素を逆に並べる「reverse(vec.begin(), vec.end())」を使えば、降順で並び替えることができる。

#include<bits/stdc++.h>
using namespace std;

int main () {
  vector<int> v = {3, 8, 6, 1, 2, 7, 5, 4};
  
  //配列の並び替え(昇順)
  sort(v.begin(), v.end());
 
  //配列を逆に並び替え
  reverse(v.begin(), v.end());

  //配列の出力
  for (int i = 0; i < v.size(); i++){
    if (i > 0) {
      cout << " ";
    }
    cout << v[i];  // => 8 7 6 5 4 3 2 1
  }
  
  return 0;
}

「sort(vec.begin(), vec.end(), greater<int>())」で降順に並び替える方法もある。

#include<bits/stdc++.h>
using namespace std;

int main () {
  vector<int> v = {3, 8, 6, 1, 2, 7, 5, 4};
  
  //配列の並び替え(降順)
  sort(v.begin(), v.end(), greater<int>());
 
  //配列の出力
  for (int i = 0; i < v.size(); i++){
    if (i > 0) {
      cout << " ";
    }
    cout << v[i];  // => 8 7 6 5 4 3 2 1 
  }
  
  return 0;
}

重複削除

配列vecをsortで並べ替えた後、「vec.erase(unique(vec.begin(), vec.end()), vec.end())」で配列vecの重複を削除する。

要素数も変わる。

#include<bits/stdc++.h>
using namespace std;

int main () {
  vector<int> v = {3, 1, 1, 2, 3, 2, 3, 1, 2};
  
  //配列の並び替え(昇順)
  sort(v.begin(), v.end());
  //配列の重複削除
  v.erase(unique(v.begin(), v.end()), v.end());

  //配列の出力
  for (int i = 0; i < v.size(); i++){
    if (i > 0) {
      cout << " ";
    }
    cout << v[i];  // => 1 2 3
  }
  
  //配列の要素数
  cout << endl << v.size(); // => 3
  return 0;
}

ビット演算

「0」と「1」のビットに関する演算。「bitset<ビット数> 変数名」でビット列を定義する。

#include <bits/stdc++.h>
using namespace std;

int main () {
  //ビット列の宣言。すべてのビットが0。
  bitset<4> bit;
  cout << bit << endl; // => 0000
  
  //指定したビット列で宣言もできる。
  bitset<4> bit1("1101");
  cout << bit1 << endl; // => 1101

  return 0;
}

ANDやORなどの演算も行うことができる。左シフトなどを行う場合は「変数 << シフトするビット数」とする。

#include <bits/stdc++.h>
using namespace std;

int main () {
  bitset<4> a("1001");
  bitset<4> b("1100");
  
  //AND
  cout << (a & b) << endl; // => 1000
  
  //OR
  cout << (a | b) << endl; // => 1101

  //左シフト
  cout << (a << 1) << endl; // => 0010
  
  //右シフト
  cout << (b >> 1) << endl; // => 0110
  
  bitset<4> c("0011");
  //NOT
  cout << ~c << endl; // => 1100
  
  return 0;
}

関数

「戻り値の型 関数名(引数1の型 引数1の名前, 引数2の型 引数2の名前, ...){}」で定義する。

#include <bits/stdc++.h>
using namespace std;

// 関数定義
int add (int a, int b) {
  return a + b;
}

int main () {
  int a = 10;
  int b = 5;
  cout << add(a, b) << endl; // => 15
}

pair

「pair<値1の型, 値2の型> 変数名」で2つの値の組を作れる。

  • 1番目の値へのアクセス:変数名.first
  • 2番目の値へのアクセス:変数名.second
#include <bits/stdc++.h>
using namespace std;

int main () {
  pair<int, string> p;
  p = make_pair(1, "one");

  //宣言と同時に値を入れることもできる。
  //pair<int, string> p(1, "one");

  cout << p.first << endl;  // => 1
  cout << p.second << endl; // => one
  
  return 0;
}

tuple

「tuple<値1の型, 値2の型, 値3の型, ・・・> 変数名」で複数の値の組を作れる。

  • K番目の要素へのアクセス:get<K>(変数名)
#include <bits/stdc++.h>
using namespace std;

int main () {
  tuple<int, string, char> t;
  t = make_tuple(11, "eleven", 'A');

  //宣言と同時に値を入れることもできる。
  //tuple<int, string, char> t(11, "eleven", 'A');

  cout << get<0>(t) << endl;  // => 11
  cout << get<1>(t) << endl;  // => eleven
  cout << get<2>(t) << endl;  // => A
  
  return 0;
}

map

「map<Keyの型, Valueの型> 」で宣言する。

#include <bits/stdc++.h>
using namespace std;

int main () {
  map<string, int> fruits;
  fruits["apple"] = 1;
  fruits["banana"] = 2;
  fruits["melon"] = 3;
  
  cout << fruits["banana"] << endl; // => 2
  
  return 0;
}

mapの要素を繰り返し処理する。要素は追加した順番ではなく、Keyで並び替えられる。

#include <bits/stdc++.h>
using namespace std;

int main () {
  map<string, int> fruits;
  fruits["banana"] = 1;
  fruits["apple"] = 2;
  fruits["melon"] = 3;
  
  for(auto item : fruits){
    cout << item.first << ", " << item.second << endl;
  }
  
  return 0;
}

//出力結果
/*
apple, 2
banana, 1
melon, 3
*/

set

setは、重複のないデータのまとまりを扱える。「set<値の型> 変数名」で宣言する。

要素は、値を追加した順番ではなく、値で並び替えられる。

  • 値の追加:変数.insert(値)
  • 要素数の取得:変数.size()
#include <bits/stdc++.h>
using namespace std;

int main () {
  set<string> fruits;
  fruits.insert("banana");
  fruits.insert("apple");
  fruits.insert("melon");
  fruits.insert("apple"); // => appleはすでに追加されているので無視される

  cout << fruits.size() << endl; // => 3

  for(auto fruit : fruits){
    cout << fruit << endl;
  }
  return 0;
}

//出力結果
/*
3
apple
banana
melon
*/

マクロ

C++は、マクロと呼ばれる機能があります。マクロを使えば、指定した文字列をプログラムの中で置き換えることができます。

「#define マクロ名 置き換えるプログラム」

これを使うことでプログラムをスッキリ書くことができます。

#include <bits/stdc++.h>
using namespace std;

//マクロの定義
#define rep(i, n) for (int i = 0; i < n; i++)

int main () {
  vector<int> v = {1, 10 ,100};
  
  rep(i, v.size()) cout << v[i] << endl;
}

//出力結果
/*
1
10
100
*/

参考

C++入門 AtCoder Programming Guide for beginners (APG4b)

タイトルとURLをコピーしました