競技プログラミング(競プロ)では、計算速度が速いなどの理由から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
*/