hdu 5724 - chess (sg函数)

来源:互联网 发布:韩国电视台直播软件 编辑:程序博客网 时间:2024/05/17 21:58
<p></p><p>#include<cstdio></p>#include<cstring>#include<cmath>#include<algorithm>#include<vector>#include<map>#include<set>#include<queue>#include<stack>#include<iostream>using namespace std;typedef long long LL;int sg[1<<21];bool h[25];void init_SG(){    memset(sg,0,sizeof sg);    for(int i=(1<<20)-1;i>=0;i--)    {        memset(h,0,sizeof h);        for(int j=0;j<20;j++)        {            if((i&(1<<j))==0) continue;            for(int k=j+1;k<20;k++)            {                if((i&(1<<k))!=0) continue;                h[sg[i-(1<<j)+(1<<k)]]=1; break;            }        }        for(int j=0;j<=20;j++) if(h[j]==0) { sg[i]=j; break; }    }}int main(){    init_SG();    int T; scanf("%d",&T);    while(T--)    {        int n; scanf("%d",&n);        int ans=0;        for(int i=1;i<=n;i++)        {            int k,y=0; scanf("%d",&k);            while(k--)            {                int f; scanf("%d",&f); f--;                y=y|(1<<f);            }            ans=ans^sg[y];        }        if(ans) printf("YES\n");        else printf("NO\n");    }    return 0;}


因为一行最多只有20个数,也就是说只有(1<<20)种状态,向右移动表示小的数推向了大的数。可以用SG函数预处理出所有情况。然后把每一行的SG函数值异或一下,非零则必胜,否则输。


组合博弈的通解就是sg函数,学习了sg函数之后一直没有咋用过。

学习博弈的可以在nyoj上面做10道取石子题目,作为了对博弈也就有一定理解了。

用的时候注意初始的时候只要初始sg[0]=0;

其他都通过函数求解。

这里贴一个求解sg函数的模板。

[cpp] view plain copy
 print?在CODE上查看代码片派生到我的代码片
  1. int sg[N];  
  2. bool hash[N];  
  3. void sg_solve(int *s,int t,int N)   //N求解范围 S[]数组是可以每次取的值,t是s的长度。  
  4. {  
  5.     int i,j;  
  6.     memset(sg,0,sizeof(sg));  
  7.     for(i=1;i<=N;i++)  
  8.     {  
  9.         memset(hash,0,sizeof(hash));  
  10.         for(j=0;j<t;j++)  
  11.             if(i - s[j] >= 0)  
  12.                 hash[sg[i-s[j]]] = 1;  
  13.         for(j=0;j<=N;j++)  
  14.             if(!hash[j])  
  15.                 break;  
  16.         sg[i] = j;  
  17.     }  
  18. }  


用set容器实现的方法,原理一样。oj上容易超时

[cpp] view plain copy
 print?在CODE上查看代码片派生到我的代码片
  1. void sg_solve()  
  2. {  
  3.     memset(sg,0,sizeof(sg));  
  4.     for(int i=1;i<N;i++)  
  5.     {  
  6.         set<int> v;  
  7.         for(int j=0;j<t;j++)  
  8.             if(i - s[j] >= 0)  
  9.                 v.insert(sg[i-s[j]]);  
  10.         int g=0;  
  11.         while(v.count(g)!=0)  
  12.             g++;  
  13.         sg[i]=g;  
  14.     }  
  15. }  



通过一道题目说一下。

hdoj 1536 和pku 2960 S-Nim

题意就是给出一个数组s。为每次可以取石子的数目。

然后给你n堆石子每堆si。求解先手能不能赢!标准的sg函数用法题目。

代码:

[cpp] view plain copy
 print?在CODE上查看代码片派生到我的代码片
  1. #include<stdio.h>  
  2. #include<string.h>  
  3. #include <string>  
  4. #include <iostream>  
  5. using namespace std;  
  6.   
  7. const int N = 10008;  
  8. int s[108],t;  
  9. int sg[N];  
  10. bool hash[N];  
  11. void sg_solve(int *s,int t,int N)   //N求解范围 S[]数组是可以每次取的值,t是s的长度。  
  12. {  
  13.     int i,j;  
  14.     memset(sg,0,sizeof(sg));  
  15.     for(i=1;i<=N;i++)  
  16.     {  
  17.         memset(hash,0,sizeof(hash));  
  18.         for(j=0;j<t;j++)  
  19.             if(i - s[j] >= 0)  
  20.                 hash[sg[i-s[j]]] = 1;  
  21.         for(j=0;j<=N;j++)  
  22.             if(!hash[j])  
  23.                 break;  
  24.         sg[i] = j;  
  25.     }  
  26. }  
  27.   
  28. int main()  
  29. {  
  30.     int i,j,n,m,h;  
  31.     while(scanf("%d",&t),t)  
  32.     {  
  33.         string ans="";  
  34.         for(i=0;i<t;i++)  
  35.             scanf("%d",&s[i]);  
  36.         sg_solve(s,t,N);  
  37.         scanf("%d",&n);  
  38.         for(i=0;i<n;i++)  
  39.         {  
  40.             scanf("%d",&m);  
  41.             int res = 0;  
  42.             for(j=0;j<m;j++)  
  43.             {  
  44.                 scanf("%d",&h);  
  45.                 res ^= sg[h];  
  46.             }  
  47.             ans+=res?'W':'L';  
  48.         }  
  49.         cout<<ans<<endl;  
  50.     }  
  51.     return 0;  
  52. }  




0 0
原创粉丝点击