ACM——博弈论 (以SG的求法为主)

HDU 1847Good Luck in CET-4 Everybody! 转自:http://blog.csdn.net/jxy859/article/details/6722660

n个石子取2的次幂个,0为terminal position

P/N分析和求SG值方法都可以,找规律的话模3余0也能过

主要是为了练习SG的求法

  1. #include <cstdio>  
  2. #include <cstring>  
  3. //单纯博弈型 也可用P/N分析法  
  4. int x[12],SG[1050];  
  5. bool vis[1050];  
  6.   
  7. void init()  
  8. {  
  9.     for (int i=0 ; i<11 ; ++i)  
  10.         x[i]=1<<i;  
  11.     SG[0]=0;  
  12.     for (int i=1 ; i<1002 ; ++i)//get Sprague-Grundy value;  
  13.     {  
  14.         memset (vis , 0 , sizeof(vis));  
  15.         for (int j=0 ; x[j]<=i ; ++j)  
  16.         {  
  17.             vis[SG[i-x[j]]]=true;  
  18.         }  
  19.         for (int j=0 ; ; ++j)  
  20.         {  
  21.             if(!vis[j]){SG[i]=j;break;}  
  22.         }  
  23.     }  
  24.     for (int i=0 ; i<100 ; ++i)  
  25.     printf("num=%d sg=%d\n",i,SG[i]);  
  26. }  
  27.   
  28. int main ()  
  29. {  
  30.     int n;  
  31.     init();  
  32.     while (~scanf("%d",&n))  
  33.     {  
  34.         if(SG[n])puts("Kiki");  
  35.         else puts("Cici");  
  36.     }  
  37.     return 0;  
  38. }  


 

HDU 3980 Paint Chain

n元环,每次取连续的m个元素,最先不能取者败。

可以考虑n<m和n>=m两种情况,第一种情况结果显然,第二种将取过1次后剩下的n-m元链进行求SG值,前m-1个状态的SG值明显为0,m为1,之后的状态取走m元素之后,会将剩下的2段分成2份(考虑0的情况),分别求其SG值,从而得到当前的状态的一个后继SG值。

 

 

  1. #include <cstdio>  
  2. #include <cstring>  
  3.   
  4. const int maxn=1005;  
  5. int SG[maxn];  
  6. bool vis[maxn];  
  7.   
  8. bool sg(int n , int m)//get SG value  
  9. {  
  10.     memset(SG , 0 , sizeof(SG));  
  11.     SG[m]=1;  
  12.     for (int i=m+1 ; i<=n ; ++i)  
  13.     {  
  14.         memset (vis , 0 , sizeof(vis));  
  15.         for (int j=0 ; j<i-m ; ++j)  
  16.         {  
  17.             vis[SG[j]^SG[i-m-j]]=true;  
  18.         }  
  19.         for (int j=0 ; ; ++j)  
  20.         {  
  21.             if(!vis[j]){SG[i]=j;break;}  
  22.         }  
  23.     }  
  24.     //for (int i=0 ; i<=n ; ++i)  
  25.         //printf("%d %d \n",i,SG[i]);  
  26.     return SG[n];  
  27. }  
  28.   
  29. int main ()  
  30. {  
  31.     int cas;  
  32.     int n,m;  
  33.     scanf("%d",&cas);  
  34.     for (int I=1 ; I<=cas ; ++I)  
  35.     {  
  36.         scanf("%d%d",&n,&m);  
  37.         if(m>n)printf("Case #%d: abcdxyzk\n",I);  
  38.         else if(sg(n-m,m))printf("Case #%d: abcdxyzk\n",I);  
  39.         else printf("Case #%d: aekdycoin\n",I);  
  40.     }  
  41.     return 0;  
  42. }  


POJ 3537 Crosses and Crosses

 

 

给一个1*n的长方形格子,在格子里轮流画X,最先得到3个连续X的人即为winner

  1. #include <cstdio>  
  2. #include <cstring>  
  3. const int maxn=2005;  
  4. int SG[maxn];  
  5. bool vis[maxn];  
  6.   
  7. void sg()  
  8. {  
  9.     memset (SG , 0 , sizeof(SG));  
  10.     SG[1]=1;SG[2]=1;SG[3]=1;//SG[4]=1;  
  11.     for (int i=4; i<maxn ; ++i)  
  12.     {  
  13.         memset (vis , 0 , sizeof(vis));  
  14.         for (int j=2 ; j<i ; ++j)  
  15.         {  
  16.             if((i-j-1<2))vis[SG[j-2]^0]=true;  
  17.             else vis[SG[j-2]^SG[i-j-1-2]]=true;  
  18.         }  
  19.         for (int j=0 ;  ; ++j)  
  20.             if(!vis[j]){SG[i]=j ; break;}  
  21.     }  
  22.     //for (int i=0; i<50 ; ++i)  
  23.     //printf("SG%d=%d\n",i,SG[i]);  
  24. }  
  25.   
  26. int main ()  
  27. {  
  28.     sg();  
  29.     int n;  
  30.     while (~scanf("%d",&n))  
  31.     {  
  32.         if(SG[n])puts("1");  
  33.         else puts("2");  
  34.     }  
  35.     return 0;  
  36. }  


 

 

POJ 2425 A Chess Game
在DAG上搜索的NIM游戏,其实和在多堆石子里拿东西是一样的,要熟练各种游戏模型之间的转化,找出更新SG的方法就可以了
一开始清空vis和visit的时候 把visit的大小写成了sizeof(vis) DT了一天才发现Bug >< 
  1. #include <cstdio>  
  2. #include <cstring>  
  3.   
  4. const int maxm=2005000;  
  5. const int maxn=1005;  
  6. struct Edge {  
  7.     int v,next;  
  8. }edge[maxm];  
  9. int head[maxn],cnt;  
  10.   
  11. void addedge(int u,int v)  
  12. {  
  13.     edge[cnt].v=v;  
  14.     edge[cnt].next=head[u];  
  15.     head[u]=cnt++;  
  16. }  
  17.   
  18. bool vis[maxn],visit[maxn][maxn];//for dfs   for sg;  
  19. int SG[maxn];  
  20.   
  21. void dfs (int u)  
  22. {  
  23.     if(~SG[u]) return ;  
  24.     //vis[u]=true;  
  25.     int p=head[u];  
  26.   
  27.     for ( ; ~p ; p=edge[p].next)  
  28.     {  
  29.         int v=edge[p].v;  
  30.         //printf("%d ",u);  
  31.         //if(vis[v])continue;  
  32.         dfs(v);  
  33.         visit[u][SG[v]]=true;  
  34.     }  
  35.   
  36.     for (int i=0 ; true ; ++i)  
  37.     {  
  38.         if(!visit[u][i]){SG[u]=i;break;}  
  39.     }  
  40. }  
  41.   
  42. int main ()  
  43. {  
  44.     int n,node,v,m,t;  
  45.     while (~scanf("%d",&n))  
  46.     {  
  47.         memset (head , -1 , sizeof(head));  
  48.         memset (SG , -1 , sizeof(SG));  
  49.         cnt=0;  
  50.         for (int u=0 ; u<n ; ++u)  
  51.         {  
  52.             scanf("%d",&node);  
  53.             if(!node)SG[u]=0;  
  54.             for (int i=0 ; i<node ; ++i)  
  55.             {  
  56.                 scanf("%d",&v);  
  57.                 addedge(u , v);  
  58.             }  
  59.         }  
  60.         memset (vis , false , sizeof(vis));  
  61.         memset (visit , false , sizeof(visit));  
  62.         for(int i=0 ; i<n ; ++i)  
  63.         {  
  64.             if(vis[i])continue;  
  65.             dfs(i);  
  66.         }  
  67.         /*for (int i=0 ; i<n ; ++i) 
  68.         { 
  69.             printf("%d\t",SG[i]); 
  70.         }*/  
  71.         while (scanf("%d",&m),m)  
  72.         {  
  73.             int nim=0;  
  74.             for(int i=0 ; i<m ; ++i)  
  75.             {  
  76.                 scanf("%d",&t);  
  77.                 nim^=SG[t];  
  78.             }  
  79.             puts(nim?"WIN":"LOSE");  
  80.         }  
  81.     }  
  82.     return 0;  
  83. }  
POJ 1704 Georgia and Bob( 阶梯博弈(目标的NP态只和奇数阶的NP态有关)
 
知道如何转换的话就很简单,关键是我不知道,推了半天,蒙了各种感觉有道理的转化方法都WA了,最后还是看了题解,还是很好理解的。
我的理解:对于棋盘上棋子,我们两两对其绑定,如果是奇数个,我们可以看做是和0处的一个棋子绑定,通过找规律我们可以发现,每对棋子的前一个棋子移动之后,后一个棋子可以移动和前一个棋子相同的步数是不影响结果的,即这是一个相对于比赛结果的平衡态,所以我们可以把所有的棋子对都平移到左边并保持棋子对之间的距离不变,这时我们发现我们要做得只是让每对棋子的后者和前者距离变为1,以及向前平移每对棋子,这两种操作后者是不影响结果的,于是我们将前者的操作转化成相应数值的NIM型博弈,剩下的大家就都能知道了
  1. #include <cstdio>  
  2. #include <algorithm>  
  3.   
  4. using namespace std;  
  5.   
  6. const int maxn=1005;  
  7.   
  8. int nim[maxn];  
  9. int n;  
  10. int main ()  
  11. {  
  12.     int cas;  
  13.     scanf("%d",&cas);  
  14.     while (cas--)  
  15.     {  
  16.         scanf("%d",&n);  
  17.         int ans=0;  
  18.         nim[0]=0;  
  19.         for (int i=1 ; i<=n ; ++i)  
  20.         {  
  21.             scanf("%d",nim+i);  
  22.         }  
  23.   
  24.         sort(nim,nim+n+1);  
  25.   
  26.         for (int i=0 ; i<(n+1)/2 ; ++i)  
  27.         {  
  28.             ans^=(nim[n-2*i]-nim[n-2*i-1]-1);  
  29.             //printf("%d  %d  %d\n",nim[n-2*i],nim[n-2*i-1],ans);  
  30.         }  
  31.         puts(ans?"Georgia will win":"Bob will win");  
  32.     }  
  33.     return 0;  
  34. }  

 JOJ 2520  A special queen
 
  1. #include <cstdio>  
  2. #include <cstring>  
  3. int SG[105][105];  
  4. bool vis[5000];  
  5. /*棋盘上的2维SG递推,非组合游戏可以不用SG*/  
  6. /*输出格式很恶心*/  
  7. int main ()  
  8. {  
  9.     int l,w;  
  10.     SG[0][0]=0;  
  11.     for (int i=1 ; i<105 ; ++i)  
  12.     {  
  13.         SG[i][0]=i;  
  14.         for (int j=1 ; j<105 ; ++j)  
  15.         {  
  16.             SG[0][j]=j;  
  17.             if(i==j){SG[i][i]=i+1;continue;}  
  18.             memset (vis , 0 , sizeof(vis));  
  19.             for (int k=0 ; k<j ; ++k)  
  20.                 vis[SG[i][k]]=true;  
  21.             for (int k=0 ; k<i ; ++k)  
  22.                 vis[SG[k][j]]=true;  
  23.             for (int k=1 ; k<=i&&k<=j ; ++k)  
  24.                 vis[SG[i-k][j-k]]=true;  
  25.             for (int k=0 ;  ; ++k)  
  26.             if(!vis[k]){SG[i][j]=k ; break;}  
  27.         }  
  28.     }  
  29. /* 
  30.     for (int i=0 ; i<25 ; ++i) 
  31.     { 
  32.         for (int j=0 ; j<25 ; ++j) 
  33.             printf("%d ",SG[i][j]); 
  34.         puts(""); 
  35.     } 
  36. */  
  37.     while (~scanf("%d*%d",&l,&w))  
  38.     {  
  39.         if(l==1 || w==1){printf("G will win\n");continue;}  
  40.         bool flag=false;  
  41.         bool first=false;  
  42.         for (int i=0 ; i<l ; ++i)  
  43.         {  
  44.             if(!SG[i][w-1])  
  45.             {  
  46.                 flag=true;  
  47.                 if(first)printf(" ");  
  48.                 printf("(%d,%d)",i,w-1);  
  49.                 first=true;  
  50.             }  
  51.         }  
  52.         for (int i=0 ; i<w-1 ; ++i)  
  53.         {  
  54.             if(!SG[l-1][i])  
  55.             {  
  56.                 flag=true;  
  57.                 if(first)printf(" ");  
  58.                 printf("(%d,%d)",l-1,i);  
  59.                 first=true;  
  60.             }  
  61.         }  
  62.         if(!flag)printf("G will win");  
  63.         printf("\n");  
  64.     }  
  65.     return 0;  
  66. }  

 
 
ZOJ Monthly, September 2011  A Game Between Alice and Bob 博弈论
nim的变形,合数分解,被常数给坑了
  1. #include <cstdio>  
  2. #include <cstring>  
  3. #include <cmath>  
  4. #define abs(a) (a)>(0)?(a):(-(a))  
  5. #define  min(a,b) (a>b?b:a)  
  6.   
  7. const int maxn=100000+123;  
  8. typedef int  typen;  
  9. typen a[maxn];  
  10. typen count[maxn];  
  11.   
  12. const int maxp = 5000;  
  13. int prime[maxp + 1];//prime[0] is the counter  
  14. int getPrime()  
  15. {  
  16.     memset (prime, 0, sizeof (prime));  
  17.     for (int i = 2 ; i <= maxp ; i++)  
  18.     {  
  19.         if (!prime[i]) prime[++prime[0]] = i;  
  20.         for (int j = 1; j <= prime[0] && prime[j] * i <= maxp ; j++)  
  21.         {  
  22.             prime[prime[j]*i] = 1;  
  23.             if (i % prime[j] == 0) break;  
  24.         }  
  25.     }  
  26.     return prime[0];  
  27. }  
  28.   
  29. int getFactors(typen x)  
  30. {  
  31.     int facCnt = 0;  
  32.     typen tmp = x;  
  33.     for(int i = 1; prime[i]*prime[i] <= tmp ; ++i)  
  34.     {  
  35.         if(tmp % prime[i] == 0)  
  36.         {  
  37.             while(tmp % prime[i] == 0)  
  38.             tmp /= prime[i],++facCnt;  
  39.         }  
  40.     }  
  41.     if(tmp != 1)  
  42.         ++facCnt;  
  43.     return facCnt;  
  44. }  
  45.   
  46. int cas=0;  
  47. int main ()  
  48. {  
  49.     int n;  
  50.     getPrime();  
  51.     while ( ~scanf("%d",&n) )  
  52.     {  
  53.         memset (count , 0 , sizeof(count));  
  54.         int sum=0;  
  55.         for (int i=0 ; i<n ; ++i)  
  56.         {  
  57.             scanf("%d",a+i);  
  58.             count[i]=getFactors(a[i]);  
  59.         }  
  60.         for (int i=0 ; i<n ; ++i)  
  61.         {  
  62.             sum^=count[i];  
  63.         }  
  64.         if(sum)  
  65.         {  
  66.             int i;  
  67.             for (i=0 ; i<n ; ++i)//从  
  68.             {  
  69.                 if((sum^count[i])<count[i])break;  
  70.             }  
  71.             printf("Test #%d: Alice %d\n",++cas,i+1);  
  72.         }  
  73.         else printf("Test #%d: Bob\n",++cas);  
  74.     }  
  75.     return 0;  
  76. }  
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值