この記事は NSSOL Advent Calendar 2019 の4日目の記事です。
TL;DR
https://github.com/sekikatsu36/AtCoder
↑こちらをfork
概要
この記事では「C#使いの競技プログラミング未経験者が、C#で初AtCoderに挑むハードルを下げる」ことを目的に、ベースとなるVSプロジェクトの共有&紹介を行います。
コンテストを続けるための便利TIPSは他にも山ほどあるので、気になればぜひ調べててみてください。(末尾に参考URLを記載)
背景
AtCoderはいわゆる競技プログラミングコンテストのサービスです。
今更ですが、私も最近AtCoderを始めまして。(まだ茶色レベルの新米です)
初回にC# + Visual Studioで参戦した際、「事前にVSプロジェクトとか準備した方がいいんだろうなぁ」と思ってはいたのですが、それを怠ってぶっつけ本番で突撃して見事なまでに惨敗。
何度か再戦し、最近それっぽいプロジェクトが出来上がったので、この場を借りて共有します。
「競プロをC#で!」みたいな記事は世に溢れてますが、VSプロジェクトは転がっていないようだったので、この期に配布。
私のような悔しい思いをする方が一人でも減ればと。
なんでC#?
C#が好きだから
その一言に尽きるんじゃないでしょうか。
一応上げると、以下のようなメリットがあります。
- LINQが便利(ただしOrderByは使ってはならぬ。遅すぎ)
- Visual Studioが神IDE
- ジェネリックが使える
ただ、ぶっちゃけ、AtCoderでワザワザC#を選ぶ必要はないです。
C#には以下のようなデメリットがあるので、PythonやC++が得意ならそっちの方が良いと思います。
- 競プロで便利なコレクション操作の一部が標準装備されていない(vs Python)
- 遅い (vs C++)
- 人口が少ない
できあがったもの
以下にあります。
適宜forkしてください。
使い方
A~Fまで、すべての問題が一プロジェクトに収まってるので、masterからブランチを切れば即コンテストを始められます。
ライブラリを作りたいときは、それ用のクラスを適当に追加すれば良いので、管理は楽だと思います。
コンテスト中にエントリクラスやライブラリに変更を加えた際は、そのファイルだけmasterにmergeすれば次のコンテストですぐ使えます。
git checkout abc146 -- .\AtCoder\Program.cs
問題を切り替えるときはProgram.cs内で呼び出すクラスを書き換える必要があります。
そこはイケてないのですが、良い方法が思いつかず。
まぁ何かしらの方法で切り替える必要はあると思うので、今はこれで妥協してます。
妙案あったらシェアいただけると嬉しいです。
中身の紹介
無限ループで繰り返し実行できる
まぁ言うまでもなく。
これくらいパパっと実装できるとは思いますが、事前に用意してあるとストレスが全然違います。
初心者が一番最初に欲しくなる機能だと思います。
スタートアップオブジェクトを指定して、コピペで提出できるようにする
すべての問題をそのまま一プロジェクトにまとめると、エントリポイントの問題が出てきます。
そのまま各問のクラスにMainメソッドを追加すると「エントリポイントが複数定義されてるぞ」とVisual Studioの怒りを買ってしまいます。
かといって、Main以外の名前にしてしまうと、コードをコピペして提出した時にAtCoderから「エントリポイントが見つからない」と心無いことを言われてしまいます。
プロジェクトを分けるのもコードを共通化しにくい。
そんな時はプロジェクトのプロパティから**[アプリケーション]>[スタートアップオブジェクト]を設定**しましょう。
複数のクラスでMainメソッドが定義されていても、これを指定すれば普通に実行できます。
各問の最初によくつかう標準入力処理を書いておく
AtCoderは、テストケースを標準入力から受け取る仕様です。
競プロ勢からすれば標準入出力なんて手慣れたものでしょうが、初心者は一瞬手が止まると思います。(標準入力とか普段使わないし)
出鼻を挫かれるのも何なので、最初から載せておきます。実際の問題に合わせて、追加・削除します。
// 文字列の入力
string s = Console.ReadLine();
// 整数の入力
long n = long.Parse(Console.ReadLine());
// 文字列配列の入力
string[] inputStrArray = Console.ReadLine().Split(' ');
// 整数配列の入力
long[] inputLongArray = Console.ReadLine().Split(' ').Select(i => long.Parse(i)).ToArray();
ちなみに、intを使ってメモリをケチる必要はないです。
これまで10回弱参加しましたが、メモリが問題になったことはほぼなかったので。
予期せぬ桁溢れの方がずっと怖いので、脳みそを止めてlongを使います。(メモリは溢れてから考えるスタイル)
なお、「入力を高速化」みたいなTIPSも世には転がってますが、最初はそこまで神経質になる必要はないかな、と。
各問の最後に標準出力処理を書いておく
AtCoderは、結果を標準出力から返す仕様です。
入力同様、出力処理も記載しておきます。
C#のConsole.WriteLineは遅いので、Mainの開始時にAutoFlushをfalseにセットして、
var sw = new System.IO.StreamWriter(Console.OpenStandardOutput()) { AutoFlush = false };
Console.SetOut(sw);
最後にFlush処理を行います。
Console.Out.Flush();
こうすれば少しだけ高速化できます。(と偉い人が言っていた。自分で速度計測したわけではないです orz)
結果の出力をFlushの前に書くことだけ意識的に。
オーバーフローを例外化する
初心者がやらかすミスの一つがオーバーフロー(桁溢れ)だと思います。
私も何度も痛い目を見ました。
前項の通りintを使わないことである程度回避できますが、それでも食らうときは食らうので、ローカル環境ではオーバーフローが発生したら例外が出るようにしましょう。
プロジェクトのプロパティで、[ビルド]>[詳細設計]>[演算のオーバーフロー及びアンダーフローのチェック]をONにします。
ライブラリを用意しておく(任意)
ライブラリを持っておくと、解くスピードがぐっと上がる可能性があります。
が、個人的に、
- 事前に揃えておいても、初心者はどうせ使いどころが分からない
- 一度自分で実装しようとしてみた方が勉強になる
という理由から、特にはじめは意識しなくてもいいと思います。
とはいえ、C#でAtCoderをやっていれば、今に以下のようなライブラリが欲しくなってくると思うので、「欲しいな」と思ったタイミングでライブラリ化しましょう。
- 優先度付きキュー。合わせてダイクストラも。
- UpperBound, LowerBound
- gcd(最大公約数)
- 素因数分解
- nCr
使うときはLibを直接呼び出すのではなく、各問のクラスにコピペするよう注意。
その他TIPS
他にも、以下のようなことをやっておくと、便利かもしれません
- コードスニペットを覚えておく https://qiita.com/Kosen-amai/items/248e44dff958be901a84
- for/foreach/cwはよく使います
- 他にもほしいものがあれば、Visual Studioを使っている利点をフル活用して、事前登録しておくよろし
- 問題のURLを控えておく
- コンテスト開始時はアクセスが集中するのでページ表示が遅いです
- 問題の一覧 → A問題表示、と画面遷移すると若干待たされるので、最初からA問題に飛べるリンクを用意しておくと気が楽です(その数秒を争うレベルなのか?という疑問は捨て置く)
- e.g. https://atcoder.jp/contests/abc141/tasks/abc141_a