トラブルシューティング
問題文をよく読む
添字は0-originか1-originか
'L', 'R', 'U', 'D'等の記号がどの向きと対応しているのか確認する(ちゃんと問題分を読まないと暗に「i,jが増える方向がそれぞれ'U','R'である」と考えがち)
そもそもロジックが間違っていないか?
式変形が間違っていないか?
ありがちなWA
入力がオーバーフローしている場合がある。この場合、そもそも答えが正しいか検証できない
main関数でreturnしていなくてもコンパイルは通ってしまうことがある。
実数を出力するとき、必ずfixed、setprecisionを設定する(桁が多くなると勝手に指数表記にされるため)
イテレート中のコレクションを破壊しないように気をつける
// NG
rep(i, n) rep(j, n) {
if (j != a) {
follow[a][j] = (follow[a][j] || follow[a][i] && follow[i][j]);
}
}
// OK
vector<bool> tmp(n, false);
rep(i, n) rep(j, n) {
if (j != a) {
tmp[j] = (tmp[j] || follow[a][i] && follow[i][j]);
}
}
異常に実行が遅い
適切なアルゴリズムを実装しているはずなのに処理が重いという場合、
stderrへの出力はジャッジシステムには読み捨てられるが、出力処理自体はされるので実行時間に響く場合がある。
コーディングスタイル
全ての箇所で64bit整数を使う
暗にint8_tの使用を禁止する
値の範囲に気を付ける a,bが共にd桁でも、a+bがd桁におさまるとは限らない!
NやQといった数で配列のサイズを初期化しない。ただし、要素がアクセスされるタイミングが事前にわからない場合を除く。←なぜ?理由があったはずだが思い出せない
簡潔に書く
競プロのコードは式がごちゃごちゃしがちなので、なるべくトークン数を削減して読みやすくする必要がある
入れ子のコンテナを避ける
入れ子のコンテナはバグを生みやすい。
代わりにstd::mapを使う
vector<vector<int>> group;
// ...
rep(i, N) {
if (!group.empty()) { // wrong
// ...
}
}
rep(i, N) {
if (!group[i].empty()) { // ok
// ...
}
}
少ないデータを持つ
よくdpテーブルなどを必要以上に確保してしまうが、添字が多くなると読みづらくなる。
1ステップ前の状態しか参照しないのなら、dpとdpOldの2つの変数を定義すれば十分
コレクションへの参照を使う
rep(i, n) {
const auto& vec = dp[i];
// do something
}
区間を表す数値の扱い
境界を含む・含まないを明示する
const auto begin = c_count.begin() + left;
const auto end = c_count.begin() + right + 1;
const auto val = c_count[used_count] + 1;
const auto it = lower_bound(begin, end, val);
コメントを書く
↓みたいな微妙に気づきづらいミスに気づく方法
fact[n+k] * inv(n) * inv(k) // wrong
fact[n+k] * inv(fact[n]) * inv(fact[k]) // ok
呼び出されるはずの関数が呼ばれていない場合がある
マジックナンバーを避ける
初期値は間違っていないか?(適当に0とか入れない) 数値リテラルをそのまま使っている場所は危ない。意味のある名前を持つ定数を用いる
int max_value = 0; // ng
for(...){
max_value = max(max_value, ...)
}
int max_value = -INF // ok
意味のある名前を与える
変数の見た目が似すぎて間違える(nとmなど)ような事態を避けることができる。