HDU 5724 Chess

题目:Chess

链接:http://acm.hdu.edu.cn/showproblem.php?pid=5724

题意:有一个n行20列的棋盘,棋盘上分布着一些棋子,A、B两人轮流下棋,A先手,每次操作可以将某个棋子放到自己右边的第一个空位(也就是说右边如果已经有子,可以跳过它,没有就右移一步),但最多20列,绝对不能超过棋盘,无棋可走的输。

思路:

  典型的博弈论,太久没做这种题了,比赛的时候居然忘了sg值,单纯在弄必胜必败了。

  首先看一行的情况下,有子记为1,无子记为0,那么一行的棋子分布就可以看作一个二进制数,所有情况就是1到2^20-1。那么我们用一个u[i]来表示i这种情况的sg值,1是必败点,u[1]=0,然后我们遍历2到2^20-1,判断每种情况可能一步到达的情况,比如10010,下一步可能是01010,10001,如果下一步都是必胜点,那么这一点肯定是必败的sg值为0,否则计算它的sg值,也就是不属于所有下一种情况的sg值且最小,比如下一步的sg值有0,1,2,4,5,那么不属于这些的又最小的就是3。之所以可以从2遍历到2^20-1,原因就是下一种情况肯定不比现在的情况大(这一点很重要)。

  单行的解决了,多行只要把每一行的sg值异或一下就可以了。

 1 #include<stdio.h>
 2 #include<string.h>
 3 int u[1050000];
 4 int dd[100];
 5 void solve(int x)
 6 {
 7   int tmp=x;
 8   int bt[100],bo=0;
 9   while(x)
10   {
11     bt[bo++]=x%2;
12     x/=2;
13   }
14   int pos=-1;
15   memset(dd,0,sizeof(dd));
16   for(int i=0;i<bo;i++)
17   {
18     if(bt[i]==1&&pos==-1);
19     else if(bt[i]==1) dd[u[tmp+(1<<pos)-(1<<i)]]=1;
20     else pos=i;
21   }
22   for(int i=0;i<100;i++)
23     if(!dd[i])
24     {
25       u[tmp]=i;
26       break;
27     }
28 }
29 void Init()
30 {
31   u[0]=0;
32   u[1]=0;
33   for(int i=2;i<=(1<<20);i++)
34   {
35     solve(i);
36   }
37 }
38 int main()
39 {
40   Init();
41   int n,t,m,x;
42   scanf("%d",&t);
43   while(t--)
44   {
45     int s=0;
46     scanf("%d",&n);
47     for(int i=0;i<n;i++)
48     {
49       scanf("%d",&m);
50       int o=0;
51       for(int i=0;i<m;i++)
52       {
53         scanf("%d",&x);
54         o=o+(1<<(20-x));
55       }
56       s^=u[o];
57     }
58     if(s) printf("YES\n");
59     else printf("NO\n");
60   }
61   return 0;
62 }

 

posted @ 2016-07-20 10:54  hchlqlz  阅读(498)  评论(0编辑  收藏  举报