C++でNimをつくった
C++で某石取りゲームNimを作りました. Nimのコンパイラを作ったわけではありません... CPUと対決するようなものを作りました.
付録のソースコードをコンパイルしてお楽しみください.
開発環境の都合で表示がバグったりするかもしれないですが勘弁.
使い方
- 起動します
- なにも始まりませんが, 石列の数(数値)を入力します(面倒なので数値以外を入力することを想定していません...)(1~30ぐらいが安全です. )
- ゲームが始まります(必ず先手でスタートします
順番ぎめが面倒だったので) - 最後の石をとると勝ちです. がんばりましょう. CPUは一瞬で石を取るのですぐに自分の番が来ます.
各石列が16個までなのは開発環境の都合です.
クラスとかメソッドとかいろいろ学べてよかったです. あまり活かせてませんが.
にしてもグラフィカルなUIはめんどいっすね〜めんどいめんどい.
コンパイルオプション
g++ -std=c++11 -o nim nim.cpp -lglut -lGLU -lGL
スパゲッティなソースコードは続きから
続きを読むARC103 反省
CとDの部分点の1.5完. とれるところは全部とれたと思う. けど立ち回りがよくなかった.
C問題
問題
解法
偶数添字と奇数添字でグループ分けできる. それぞれで最も数が多い数字にするのが最善であるが, その数字が同じだったときの処理が面倒(最後のサンプルで気づいた).
具体的には, 数字の数が最も多い数字を記録しておき, プリオリティーキューに偶数番目と奇数番目の数字の数を別々のものにいれておき, 数字が一致してしまったときに, どっちかのキューの2番めに大きいものと交換して差が小さくなる方を選ぶと最善.
感想
コンテスト中はコーナーケースあるのでは?とか考えてちょっと提出をためらってしまいました. 実装も面倒で面倒な問題です.
D問題
部分点のみで, 後で満点は解き直します.
解法
部分点解法のみ.
まず, がんばって問題文を読解します.
各について, を考えます. この値が異なるものが存在したとき, どうやっても答えを構築することはできません.
なぜならば, をどのように設定してどのように足しあわせても, 各の和の偶奇は不変で, 偶数グループまたは奇数グループについて, 移動しえない場所が存在してしまいます.
上の条件を満たしていると考えると, すべてのについて, としたとき, 部分点の制約ならば十分小さいで構築が可能であることがわかります.
具体的には,
のとき, として目的の場所まで真っ先に移動し, 反復横飛びでもして残りの移動を費やします.
のとき, として目的の場所まで真っ先に移動し, 反復横飛びでもして残りの移動を費やします.
これを実装して300点です.
600点はまた今度...
感想
和の偶奇とかいうの, 結構当てずっぽだったんですけどヒットしたみたいでよかったです.
総評
Cを遅解きしたあと順位表を見てみると, 誰もDをやっていなかったのでEを見たんですが, 結局は解法は生えずに結構な時間を費やしてしまったので, 結果論ではありますがDをもっと速く考えていればレートは伸びたかなーと思います.
コンテスト中は自分の身の丈にあった問題を考察していきたい.
rating 1089->1134
ABC 110 反省
ABDの3完.
A問題
解法
条件から, どれか一個だけ10倍して足すことができるのと同値になる. よって, が答え.
感想
Aにしてはちょっとむずかしい.
B問題
解法
ならば'No War' ,そうでなければ'War'です. 本番ではmin\{y\}とmax\{x\}だけ調べてかどうかを見て, を満たすが存在するかどうかはそこにさらにループを回して調べました.
今考えると, 無意味ですし実装が面倒になるだけだと思います. 実際, 実装のミスで1WAしています. (リジャッジ後もWAのままでした...)
感想
これもなんか問題文が読みにくく, Bにしてはちょっと面倒です.
C問題
解法
コンテスト中に解けず. 考えていたのは, あるアルファベットからアルファベットへの対応を調べ, 2つ以上に対応するものがあればNo, そうでなければYes, というロジックだったのですが, 実装も考察もまずくWAを連発.
WA解答では無向グラフのように対応を双方向に一気に定めていたため, ハッシュマップがめちゃくちゃになってしまっていたようです.
書き直した解答では, 対応する文字が2つ以上あったときにNoを出力するのは同じですが, あとで対応先の文字がただ1つのアルファベットのみに対応していることを確認するようにしています.
前者がアルファベットからアルファベットへの単射であるかの確認, 後者がその対応がアルファベットへの全射であることの確認となっています.
感想
えーこれ難しくないですか><.
D問題
解法
『素因数をいくつかの箱に入れて分割する』というのは, ARC-004D
D - 表現の自由 ( Freedom of expression )で見ていた(解いていませんでしたが)ので, 分割数やらで掛け合わせるのかなとコンテスト中は, ベル数やらスターリング数やらを調べていましたが, 結局素因数を個の箱に分配するときの重複組み合わせでDPができることに気づき, それを実装しました.
今考えると, ARC-004Dの解答を見れば早かったですね...
DPは,
とすると,
となります.
前後のみを見ているdpなので0次元DPに落とせます.
制約の中では, 素因数の数はたいして大きくならず, コンビネーションの前処理は定数なので, この問題の計算量はの素因数分解がネックとなり, となります.
実は本番のコードはの素因数分解をしてしまっています...
しかし, 通ってしまいました.
提出
(コンテスト時提出したもの)
Submission #3257928 - AtCoder Beginner Contest 110
(に改善したもの)
Submission #3270638 - AtCoder Beginner Contest 110
実に800倍の速度の差が出ています.
感想
ささっと思いつけてよかった.
総評
B問題に不備があり, 結果unratedとなりましたが, 初めて推定パフォ1600上限を出せたのでよかったです.
ABC 041-D 徒競走
問題
解法
のときの部分点はnext_permutationを使うと獲得できます.
満点解法では通りを考える必要があるので, 別の解法を考える必要があります.
と, うさぎの集合を見るようにすると数が小さいです.
これに着目して, bitDPを行うことを考えます.
とすると,
基底
遷移
)
とかけます. 遷移においてのというのは, 与えられたについて, 最後尾がであるが, よりの方が速いといったときには矛盾を孕むことになるので, そのような場合の数は存在せずとなる, ということです.
感想
についての制約をよく見ておらずに領域を十分にとっておらず, ACときどきWA状態でオンオンしてました. 領域足りてなかったらREでてほしいです...(我儘).
ABC 097 C - K-th Substring
ABC 098 D - Xor Sum 2
最近しゃくとり方を実装していなかったので解き直しました.
解法
問題を見るに, いかにもなしゃくとり法です.
この問題を解くうえで, xor演算の重要な性質を用います.
xor演算において以下の性質が成り立ちます.
1の性質は, この演算においては自身が逆元となることを示しています. (自明に0が単位元となっています.)
2の性質は, 可換な演算であることを示しています.
3の性質は, 結合法則が成り立つことを示しています.
これを利用すると, しゃくとり法を実装することができます.
具体的には, 以下のアルゴリズムを適用することができます.
入力の配列を, サイズをとします.
- 初期化 :
- ならばを出力して終了する.
- ならば4へ, そうでなければ5へ
- , , , 2へ
- , , 2へ
しゃくとりの右端を右に進めるときに答えを加算するようにしています.
計算量はで済みます.
感想
しゃくとり法は本番でバグらせやすいので演習を積んでいきたいです.
ABC 109 反省
久々にコンテスト中に全完できました. しかし, B問題でWAを連発してしまったため, 大きくペナルティを食らってしまったのは反省事項です.
A問題
問題
解法
を1~3まで試します.
感想
素直に実装しましたが, が奇数だったら"Yes", そうでなければ"No"です.
B問題
解法
次の文字の先頭と今みている文字の尻が一致しているかをみるついでに, map
感想
yukicoder解いてるときにWAが出るとすぐテストケースをのぞいてしまう癖があるので, こういう不測のWAに弱いとこがありました. 反省します.
C問題
問題
解法
すべてのに対しての差分を取り, これらすべての最大公約数が答えとなります.
各の間の距離がの倍数であり, さらにから距離でステップしてどれかのに到達することができれば, で数列間を移動することができます. その中での最大のは, 各に対してすべての最大公約数となります.
感想
解法はなんとなく思いついたのですが, 説明がなんとなく難しいです.
D問題
解法
発想が必要になる問題です.
まず, の制約から, の解法が求められます.
これがちょっとしたヒントで, 何度もバックトラックして計算するようなことはなさそうであることがわかります.
操作について考察します.
できるだけ偶数マスを増やしたいときは, 奇数マスのコインをどこかに動かせばよいのですが, ここで動かす方向を下方向に固定してみます.
まず1行目を左から右に見て奇数マスのコインを下に動かし,
次に2行目を...として,最後から2番目の行まで繰り返します.
すると, 最後の1行以外のマスをすべて偶数マスにすることができます.
最後の1行については, 奇数マスのコインを順次右側に動かしていくと, 一番右下のマス以外すべて偶数のマスにすることができます.
コインの総数が奇数だった場合, どうしても奇数のマスは1つできます.
コインの総数が偶数だった場合, すべて偶数のマスにできます. このとき, 右下のマスが奇数で他のマスが偶数ということはありえないので, これが最適解であることがわかります.
あとはpairとvectorなどで移動元と移動先を管理し, 丁寧に実装しましょう.
0-indexedで出力してしまい1-WA.
感想
0-indexedで出力してしまったのはちょっとした悔いですが, 解けてよかったです.
総括
最近難しいABCが続いてましたが, 今回は易しかったように思います. ただ, こういうときにWA量産して順位落としちゃうのはよくないですね.