bzoj 1874: [BeiJing2009 WinterCamp]取石子游戏
Description
小H和小Z正在玩一个取石子游戏。 取石子游戏的规则是这样的,每个人每次可以从一堆石子中取出若干个石子,
每次取石子的个数有限制,谁不能取石子时就会输掉游戏。 小H先进行操作,他想问你他是否有必胜策略,如果有
,第一步如何取石子。
Input
输入文件的第一行为石子的堆数N
接下来N行,每行一个数Ai,表示每堆石子的个数 接下来一行为每次取石子个数的种类数M
接下来M行,每行一个数Bi,表示每次可以取的石子个数,
输入保证这M个数按照递增顺序排列。
N≤10 Ai≤1000
对于全部数据,M≤10,Bi≤10
Output
输出文件第一行为“YES”或者“NO”,表示小H是否有必胜策略。
若结果为“YES”,则第二行包含两个数,第一个数表示从哪堆石子取,第二个数表示取多少个石子,
若有多种答案,取第一个数最小的答案,
若仍有多种答案,取第二个数最小的答案。
Sample Input
4
7
6
9
3
2
1
2
7
6
9
3
2
1
2
Sample Output
YES
1 1
Hint
样例中共有四堆石子,石子个数分别为7、6、9、3,每人每次可以从任何一堆石子中取出1个或者2个石子,小H有
必胜策略,事实上只要从第一堆石子中取一个石子即可。
1 1
Hint
样例中共有四堆石子,石子个数分别为7、6、9、3,每人每次可以从任何一堆石子中取出1个或者2个石子,小H有
必胜策略,事实上只要从第一堆石子中取一个石子即可。
经典的博弈论:有向图游戏和。
我们计算出每个有向图的sg函数,然后全部异或起来,如果不是0,说明当前局面必胜,否则必败。
枚举方案我们也可以从小到大枚举,看什么时候异或值为0。
1 #include<bits/stdc++.h> 2 using namespace std; 3 int const N=1000+10; 4 int n,m,a[11],sg[N],b[11],g[N]; 5 void dfs(int x){ 6 if(g[x]) return ; 7 g[x]=1; 8 int vis[12]; 9 memset(vis,0,sizeof(vis)); 10 for(int i=1;i<=m;i++) 11 if(x>=b[i]) { 12 dfs(x-b[i]); 13 vis[sg[x-b[i]]]=1; 14 } 15 for(int i=0;i<=10;i++) 16 if(!vis[i]){ 17 sg[x]=i; 18 break; 19 } 20 } 21 22 int main(){ 23 scanf("%d",&n); 24 for(int i=1;i<=n;i++) 25 scanf("%d",&a[i]); 26 scanf("%d",&m); 27 for(int i=1;i<=m;i++) 28 scanf("%d",&b[i]); 29 for(int i=0;i<=1000;i++) 30 dfs(i); 31 int ans=0; 32 for(int i=1;i<=n;i++) 33 ans^=sg[a[i]]; 34 if(ans){ 35 puts("YES"); 36 for(int i=1;i<=n;i++) 37 for(int j=1;j<=m;j++){ 38 if(a[i]>=b[j] && (ans^sg[a[i]]^sg[a[i]-b[j]])==0){ 39 printf("%d %d\n",i,b[j]); 40 return 0; 41 } 42 } 43 }else puts("NO"); 44 return 0; 45 }