2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【競プロ】あちゃぴのコーディングスタイル

Last updated at Posted at 2024-03-12

はじめに

これは競技プログラミングの C++ でのコーディングスタイルです。
筆者のコーディングスタイルは SSRS さんのコーディングスタイルをリスペクトし,独自の形式にしています.それらをわかりやすく言語化してまとめます.

テンプレ

テンプレ
#include <bits/stdc++.h>
using namespace std;
int main(){
	
}

テンプレは上記の通りです.マクロなどは一切使いません.また,事前にライブラリを入れておくということもしません.

コードの書き方のルール

基本的なこととしてはセミコロン後,中括弧の後は必ず改行をします.またカンマの後には空白を入れ,余分な行は消します.空白はタブで統一です.

入力

例えば

入力例
N
S
T
M K
A_1 A_2,..., A_N

という入力があったとします.そしたら

int N;
cin >> N;
string S;
cin >> S;
string T;
cin >> T;
int M, K;
cin >> M >> K;
vector<int> A(N);
for (int i = 0; i < N; i++){
	cin >> A[i];
}

となります.解説すると入力の一行ごとに変数宣言→入力という風にやっています.

中括弧

if (){
	...
}
for (int i = 0; i < N; i++){
	...
}
while (true){
	...
}

このように if のような文字の後は空白を入れ,括弧と中括弧の間には空白を入れません.また括弧内の文字数に依らず,中括弧の後は必ず改行をします.また閉じ括弧も一行を使います.

32bit 整数 -> int

64bit 整数 -> long long

小数 -> double

ぐらいです.また小数はすべて double で統一です.

空白の細かいルール

カンマと演算子の後は必ず空白を入れます.また,<型名> 変数名のように空白を入れます.セミコロンの後は必ず改行を入れますが,for 文のように一行に書くときはセミコロンの直後に空白を入れます.

int a, b, c;
vector<int> V;
pair<int, int> P;
for (int i = 0; i < N; i++){
}
long long N = 114 * 514 + 1919 - 810;

一部例外があって,負の数であることを表すときは空白を入れません.

A = -10;
N = -N;

細かいルール集

全部大事なのでよく読んでください.

  • またはを表す演算子は || ではなく or を使います.同様にかつを表す演算子は && ではなく and を使います.

  • bool 値を表す変数は ok を使います.二つ目を使いたかったら ok2 とします.(ok は ok1 としない)

  • std::map の構造化束縛 for 文は以下のようにします.k は key,v は value という意味があります.これに限らず構造化束縛は使うようにしましょう.

map<int, int> mp;
for (auto [k, v] : mp){

}

tuple<int, long long, double> T;
auto [a, b, c] = T;
  • int にすべきところはなるべく int にします.また計算の過程で long long にしなくてはいけないときは以下のようにします.
int N;
long long ans = (long long) N * (N + 1) / 2;
  • size() などで取得した値はなるべく int にします.例えば以下のようにしないと比較の型が違うとコンパイルエラー (注意) が出るからです.(左は unsigned int で 右は int)
    注意なので忘れてても問題はないですが統一はします.
vector<int> V;
if ((int) V.size() == 0){

}

  • long long 型の定数が欲しいときは 0LL のようにします.

  • inf の値は 1e9 や 1e18 のようにします.状況によって中身は変えますがなるべくこのような形にします.const をつけてグローバル化はしなくてもいいです.

const int inf = 1e9;
const long long inf2 = 1e18;
  • 出力形式の空白区切り,改行区切りは絶対に守ります.空白区切りの時は定型文があるので参考にしてください.
vector<int> A(N);
for (int i = 0; i < N; i++){
	cout << A[i];
	if (i + 1 < N){
		cout << ' ';
    }
}
cout << endl;
要素の個数がわからない場合
vector<int> A;
for (int i = 0; i < (int) A.size(); i++){
	cout << A[i];
	if (i + 1 < (int) A.size()){
		cout << ' ';
    }
}
cout << endl;

先ほどの size() を int にするのを忘れないでください.また,空白は " " ではなく ' ' で統一です.

  • BFS のやり方も定型文があります.(これは参考程度に)
BFS
queue<int> q;
while (!q.empty()){
	int v = q.front();
	q.pop();
    ...
}

  • クエリ問題は以下のようにします.しかし,後でクエリの個数の値を使う問題のときは for 文にしてください.またこのようにクエリの種類が番号によって分けられてる場合が多いですが場合分けするように全てについて if 文を書いてください.
int Q;
cin >> Q;
while (Q--){
	int t;
	cin >> t;
	if (t == 1){

    }
	if (t == 2){
 
    }
}

  • pair 型,tuple 型を作るときは make_pair() と make_tuple() を絶対使ってください.
make_pair(1, 2);
make_tupel(3, 3, 4);
  • ACL を使うときは #include のみ文頭に書き,呼び出すときは atcoder:: をつけてください.
#include <bits/stdc++.h>
#include <atcoder/all>
using namespace std;
int main(){
	atcoder::fenwick_tree<int> fw(N);
}

また mint を使うときは以下のようにします.

#include <bits/stdc++.h>
#include <atcoder/all>
using mint = atcoder::modint998244353;
using namespace std;
int main(){
	mint A = 0;
}
  • グリッド探索するときはベクトルを持つ.
4 方向
int dx[4] = {1, 0, -1, 0};
int dy[4] = {0, 1, 0, -1};

今回の例では 4 方向のみですが,2 方向でも 8 方向でも探索する方向のベクトルを予めもって置くと実装が楽になります.

#include <bits/stdc++.h>
using namespace std;
int dx[4] = {1, 0, -1, 0};
int dy[4] = {0, 1, 0, -1};
int main(){
	int H, W;
	cin >> H >> W;
	queue<pair<int, int>> q;
	q.push(make_pair(0, 0));
	while (!q.empty()){
		auto [x, y] = q.front();
		q.pop();
		for (int i = 0; i < 4; i++){
			int px = x + dx[i], py = y + dy[i];
			if (0 <= px and px < H and 0 <= py and py < W and 他の条件){
				...
				q.push(make_pair(px, py));
			}
		}
	}
}

このように 4 方向のベクトルを持つと実装が楽になります.またグリッド上の探索はこの書き方をベースにしてください.次に探索する座標を持つ変数は px と py で,特に if 文の中身はほとんどの場合この書き方で行けます.px と py で一行でまとめて宣言してますが,こういった場合のようにセットとなる変数はまとめて宣言,代入をしてもよいです.

  • Yes No の出力は以下のようにしてください.
if (ok){
	cout << "Yes" << endl;
} else {
	cout << "No" << endl;
}
  • 1-indexed は 0 にすぐ直す.
for (int i = 0; i < M; i++){
	int u, v;
	cin >> u >> v;
    u--;
    v--;
    G[u].push_back(v);
}

またこれはグラフの一般的な入力受け取り方であり,グラフの変数名は G を使います.

  • グラフの配列の作り方.以下は有向の場合です.
重みなしグラフの場合
vector<vector<int>> G(N);
for (int i = 0; i < M; i++){
	int u, v;
	cin >> u >> v;
    u--;
    v--;
    G[u].push_back(v);
}
重み付きグラフの場合
vector<vector<pair<int, long long>>> G(N);
for (int i = 0; i < M; i++){
	int u, v, w;
	cin >> u >> v >> w;
    u--;
    v--;
    G[u].push_back(make_pair(v, w));
}

これはほとんど統一です.

  • 変数名の意味
    ans 答えを表す変数 (answer からきている)
    sum 値の合計を表す変数
    cnt 条件に当てはまる要素の個数などの集計を表す変数 (count からきている)
    mp std::map
    st std::set
    ms std::multiset
    vec std::vector (あまり使わない)

  • 一文字シリーズ
    D, d 距離
    G グラフ
    g GCD
    l LCM
    i, j, k, l この順番に for 文の添字
    l 左
    r 右
    H 縦
    W 横
    P std::pair
    S, T 文字列
    u, v 頂点
    w 重み
    c cnt の略
    あとは適当です.また例に挙げたのも他の意味で使う場合もあります.

  • std::set の要素の存在判定は find を使う方法もありますが,このように count を使いましょう.std::map にも使えます.しかし,std::multiset に使うと TLE する可能性があるのでそれに使うのは避けましょう.

if (st.count(0) > 0){

}
if (mp.count(0) > 0){

}
if (ms.find(0) != ms.end()){

}

最後に

以上になります.書き忘れているのもありますが,それはその都度更新します.誤植,質問などがあったらコメントしてください.よろしくお願いいたします。

2
1
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?