きれい
問題概要
枚のコインがあります。高橋君と青木君は、以下の操作を高橋君から始めて交互に繰り返します。
- 0 以上の整数
を選び、コインを
枚取り除く。
先に操作を行えなくなった者の負けです。両者が最適に行動するとき、どちらが勝つでしょうか?
制約
考えたこと
もしこれが ARC-D 問題に出題されたとして、冷静に「実験しよう」と思えるだろうか...
実験すると確かに見えて来る。実験するとしたら、例えば以下のように書ける (このようなゲームを実験するときの書き方についてはこの記事などに書いた)。
#include <iostream> #include <cstring> using namespace std; int dp[110000]; int iswin(long long n) { if (n == 0) return 0; if (dp[n] != -1) return dp[n]; long long eight = 1; while (eight <= n) { if (!iswin(n - eight)) return dp[n] = 1; // 相手に「負け」を渡せたら勝ち eight *= 8; } return dp[n] = 0; } int main() { memset(dp, -1, sizeof(dp)); for (int i = 0; i <= 100; ++i) { cout << i << ": " << iswin(i) << endl; } }
これを出力すると、以下のようになる
0: 0 1: 1 2: 0 3: 1 4: 0 5: 1 6: 0 7: 1 8: 1 9: 0 10: 1 11: 0 12: 1 13: 0 14: 1 15: 0 16: 1 17: 1 18: 0 19: 1 20: 0 21: 1 22: 0 23: 1 24: 0 25: 1 26: 1 27: 0 28: 1 29: 0 30: 1 31: 0 32: 1 33: 0 34: 1 35: 1 36: 0 37: 1 38: 0 39: 1 40: 0 41: 1 42: 0 43: 1 44: 1 45: 0 46: 1 47: 0 48: 1 49: 0 50: 1 51: 0 52: 1 53: 1 54: 0 55: 1 56: 0 57: 1 58: 0 59: 1 60: 0 61: 1 62: 1 63: 0 64: 1 65: 0 66: 1 67: 0 68: 1 69: 0 70: 1 71: 1 72: 0 73: 1 74: 0 75: 1 76: 0 77: 1 78: 0 79: 1 80: 1 81: 0 82: 1 83: 0 84: 1 85: 0 86: 1 87: 0 88: 1 89: 1 90: 0 91: 1 92: 0 93: 1 94: 0 95: 1 96: 0 97: 1 98: 1 99: 0 100: 1
じっと眺めると、周期 9 で、
0, 1, 0, 1, 0, 1, 0, 1, 1
を繰り返していることがわかる。証明は後に回すとして、この通りに実装すると通る。
#include <iostream> using namespace std; int ans[9] = {0, 1, 0, 1, 0, 1, 0, 1, 1}; int main() { long long n; cin >> n; if (ans[n%9]) cout << "Win" << endl; else cout << "Lose" << endl; }
証明
コイン枚数を 9 で割ったあまりが、0, 2, 4, 6 の状態が「負けの盤面」であり、1, 3, 5, 7, 8 の状態が「勝ちの番目」であるということになる。以下の 2 つを示せばいい:
「負けの盤面」を渡されたとき、何をやっても「勝ちの盤面」にしか遷移できず、必ず相手に「勝ちの盤面」を渡してしまう
「勝ちの盤面」を渡されたとき、上手く手を打つことで「負けの盤面」にして相手に渡すことができる
1. について
であるから、
の状態からどのような手を打っても、
のいずれかになってしまう。
2. について
のときは、石を 1 個とることで、
になるので OK。
のときは、まず
であることが保証されるので、
個とることができる。そして
個とることで、
になるので OK。