【BZOJ 1874:[BeiJing2009 WinterCamp]取石子游戲 】題解
阿新 • • 發佈:2022-03-16
題目連結
題目
小H和小Z正在玩一個取石子游戲。 取石子游戲的規則是這樣的,每個人每次可以從一堆石子中取出若干個石子,每次取石子的個數有限制,誰不能取石子時就會輸掉遊戲。 小H先進行操作,他想問你他是否有必勝策略,如果有,第一步如何取石子。
思路
博弈論,考慮把題目變成Nim遊戲。
把 \([0, 1000]\) 按可行操作變成一個有向圖,然後處理出它們的SG函式。
然後,把原先的每堆石子通過SG函式轉化為真正的石子,然後跑一遍Nim即可。
Code
#include<bits/stdc++.h> using namespace std; inline int read(){int x=0,f=1;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1; ch=getchar();}while(ch>='0'&&ch<='9'){x=(x<<1)+ (x<<3)+(ch^48);ch=getchar();}return x*f;} #define N 1010 int n, m, i, j, k, T; int a[N], b[N], c[N], t[N], x[N], ans; signed main() { n=read(); for(i=1; i<=n; ++i) a[i]=read(); m=read(); for(i=1; i<=m; ++i) b[i]=read(); for(i=1; i<=1000; ++i) { for(j=1; j<=m && b[j]<=i; ++j) t[c[i-b[j]]]=i; for(j=0; j<=1000; ++j) if(t[j]!=i) break; c[i]=j; } for(i=1; i<=n; ++i) x[i]=c[a[i]]; for(i=1; i<=n; ++i) ans^=x[i]; printf(ans ? "YES\n" : "NO\n"); if(ans) { for(i=1; i<=n; ++i) for(j=1; j<=m && b[j]<=a[i]; ++j) if(c[a[i]-b[j]]==(ans^x[i])) return printf("%d %d", i, b[j]), 0; } return 0; }
總結
對於這類常見的非經典Nim遊戲問題,可以通過有向圖預處理SG函式,轉化為Nim遊戲。
這種做法在簡單博弈論中很常見。