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

Sample Output

YES
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 }  
View Code

 

posted @ 2019-06-19 23:59  zjxxcn  阅读(212)  评论(0编辑  收藏  举报