競プロで出がちなREの原因をまとめる

この記事はISer Advent Calendar 2020の15日目として書かれました。 14日目の記事はfushiguさんの tf-idfについて - fushigu’s blog でした。

自分用に書いていたメモをブログのネタにしようと思います。記事の特性上、問題のネタバレを含みます。

お前は誰

sh_mugというアカウント名で競プロをやっています。 (執筆時のレート: 1965)

使用言語はC++です。

REとは

RE (Runtime Error)
プログラムの実行中にエラーが発生しました。コンパイル時に検知できなかったエラーがあります.スタックオーバーフロー、ゼロ除算などが原因です.
(引用元: Glossary - AtCoder Beginner Contest 185)

REのタイプ別分類

自分が2020年に出した全32個のREのうち、気になったものをタイプ別に分類していきます。

配列への範囲外アクセス

配列の範囲外にアクセスすると、セグメンテーション違反でRuntime Errorが出がちです。コードテストでは、終了コードが139になります。

Submission #17971002 - AtCoder Beginner Contest 182
Submission #13841995 - AtCoder Beginner Contest 169
Submission #9301969 - AtCoder Beginner Contest 142
  • 原因 for文のiの条件、配列の初期サイズなどのtypo
  • 対策 わかりやすい変数名を付ける
for (int i = 0; i < h; ++i) { ...   // OK. これを書きたかった
for (int i = 0; i < n; ++i) { ...   // NG. hとnが似ていて気付きづらい
Submission #9298906 - AtCoder Grand Contest 031
  • 原因 境界検査と論理演算の短絡評価が絡むバグ。
  • 対策 配列へアクセスする評価より左に境界検査を書かないといけない。
if (i < N && a[i] == hoge) { ...    // OK. i>=Nのときa[i]にアクセスされない
if (a[i] == hoge && i < N) { ...    // NG. i>=NでREする
Submission #15160946 - AIsing Programming Contest 2020
Submission #14809790 - Introduction to Heuristics Contest
  • 原因 インデックスをint型の配列で管理していて、配列に-1が入る可能性を考えていなかった
  • 対策 場合分けをさぼらない
Submission #17034148 - ACL Beginner Contest
Submission #16482520 - DISCO presents ディスカバリーチャンネル プログラミングコンテスト2016 本選
  • 原因 セグメント木のクエリの右端が右過ぎる
  • 対策 手っ取り早い方法としては、木の構築時は配列の幅を広めに確保する。

ゼロ除算

さすがにやらないだろうと思っていたが、やっていた。終了コードは136です。

Submission #17851781 - AtCoder Regular Contest 060
  • 原因 自作の約数列挙関数と、桁和計算関数の双方が原因になっている。詳しく説明すると、
    • -1の約数を列挙→ 0以下が入力になることを想定しておらず、-1の約数列として{1, -1} が返される
    • -1 + 1 = 0進数として桁和を計算→桁シフト時に0除算が発生
  • 対策 まず除算、剰余演算において除数が0になる場合がないか確認する。あるいは、あらかじめ十分に処理の場合分けをする。

std::sortの第三引数

Submission #16018129 - AtCoder Regular Contest 074
  • 原因 std::sortの第三引数に比較関数compを置くとき、comp(a, b) == comp(b, a) == trueを満たすような例が存在する。
  • 対策 std::sortがREの原因になる可能性に留意する。
bool b; ...
std::sort(a.begin(), a.end(), [&](int x, int y){return b^(x<y);});    // NG
// b==trueのときx==yでcomp(x, y) == comp(y, x) == trueになってしまう

参考: stackoverflow.com

その他

Submission #15397247 - Tenka1 Programmer Beginner Contest
  • 原因 提出言語の選択ミス。C++のコードをPythonとして提出した。
  • 対策 直前に普段使わない言語で提出した、コードテストを使ったときは提出言語に注意する。またはonline-judge-toolsなどの自動提出を使う。

まとめ

  • REは気を付ければ防げるようなものが多い。
  • 配列を大きめに確保しておくと何とかなることがある。
  • 場合分けをさぼらない。