EOJ 1067 石子游戲-A
甲乙兩人面對若干堆石子,其中每一堆石子的數目可以任意確定。兩人輪流按下列規則取走一些石子,遊戲的規則如下:
1. 每一步應取走至少一枚石子;
2. 每一步只能從某一堆中取走部分或全部石子;
3. 如果誰無法按規則取子,誰就是輸家。
如果甲乙兩人都採取最優的策略,請問,是甲必勝還是乙必勝。
簡單的Nim博弈,求一下異或和。
#include <bits/stdc++.h>
using namespace std;
int main()
{
int t, n, ans, tmp;
cin >> t;
while (t--)
{
scanf("%d%d", &n, &ans);
while (--n)
{
scanf("%d", &tmp);
ans ^= tmp;
}
puts(ans ? "Win" : "Lost");
}
return 0;
}
EOJ 1068 石子游戲-B
甲乙兩人面對若干堆石子,其中每一堆石子的數目可以任意確定。兩人輪流按下列規則取走一些石子,遊戲的規則如下:
1. 每一步只能從某一堆中至少要取走一個石子,最多 m 個;
2. 如果誰無法按規則取子,誰就是輸家。
如果甲乙兩人都採取最優的策略,請問,是甲必勝還是乙必勝。
類似巴什博弈,把每堆石子數量減少到m以內,則問題轉化爲無限制Nim博弈。SG(x) = x % (m+1)。
#include <bits/stdc++.h>
using namespace std;
int main()
{
int t, n, m, ans, tmp;
cin >> t;
while (t--)
{
ans = 0;
scanf("%d%d", &n, &m);
while (n--)
{
scanf("%d", &tmp);
ans ^= tmp % (m+1);
}
puts(ans ? "Win" : "Lost");
}
return 0;
}
EOJ Monthly 2018.2 B. 無聊的遊戲
給定一棵樹,兩人輪流從樹中選取一個度數不爲 0 的結點 (度數爲 0 則不與任何邊相連) 將其與其相連的邊刪去,誰最終無法刪去結點,則誰敗。
若先手有必勝策略則輸出 First,否則輸出 Second。
在樹上搜索(或者狀壓+位運算枚舉),並利用SG函數進行轉移。
#include <bits/stdc++.h>
using namespace std;
int dp[(1<<20)+5];
vector<int> g[25];
int main()
{
int n, u, v;
cin >> n;
for (int i = 0; i < n-1; ++i)
{
scanf("%d%d", &u, &v);
g[u].push_back(v);
g[v].push_back(u);
}
for (int now = (1<<n)-1; now >= 0; --now)
for (int i = 0; i < n; ++i)
if (now & (1<<i))
{
bool flag = 0;
for (int j = 0; j < g[i+1].size(); ++j)
if (!(now & (1<<(g[i+1][j]-1)))) flag = 1;
if (flag) dp[now ^ (1<<i)] |= dp[now] ^ 1;
}
printf("%s\n", dp[0] ? "First" : "Second");
}