acm pku 2234 解题报告(取子问题)

来源:互联网 发布:一条sql删除 重复数据 编辑:程序博客网 时间:2024/06/05 15:13
      题目是这样的,两个人玩一种取火柴的游戏,火柴有若干堆,某个人去取时,可以从某一堆中取若干
根,谁最后把所有的火柴取完谁就是胜者。接着给出几组数据,对于每一组数据,第一个数 n 为火柴的堆数,
接下来的 n 个数据为每一堆火柴的数量,如果最终第一个取火柴的人会获胜,则输出 Yes,否则输出 No。
      这显然是一个取子问题,如果这两个人都知道取的策略,我们可以知道结改局在取之前就可以定下来。
如果这几堆火柴是非平衡的,那么第一个取的人可以取若干根火柴使它达到平衡,到最后这个人毕然会赢。反
之,如果火柴刚开始就是平衡的,那么第一个人一取就破坏了平衡,从而让另外一个人赢了。
      怎样来判断这几堆火柴是否是平衡的呢,有人已经总结出来了,请看下面。
     

现在考虑各大堆大小分别为N1N2……Nk的一般的Nim取子游戏。将每一个数Ni表示为其二进制数(数的位数相等,不等时在前面补0):

N= as…a1a0

N= bs…b1b0

……

 N= ms…m1m0

如果每一种大小的子堆的个数都是偶数,我们就称Nim取子游戏是平衡的,而对应位相加是偶数的称为平衡位,否则称为非平衡位。因此,Nim取子游戏是平衡的,当且仅当:

as + bs + … + ms 是偶数

……

a1 + b1 + … + m1 是偶数

a0 + b0 + … + m0是偶数


具体的操作我们可以用位运算来实现:
#include <iostream>

using namespace std;

int an[]={1,2,4,8,16,32,64,128,256,512,1024,2048,4096,8192,16384,32768,65536,131072,262144,524288,1048576,2097152,4194304,8388608,16777216,33554432,67108864,134217728,268435456,536870912,1073741824};
int main()
{
      int n;
      while(cin>>n)
      {
             int str[30];
             for(int k=0;k<n;++k)
             cin>>str[k];
             int i=0;
             for(i=0;i<31;++i)
             {
                      int sum = 0;
                      for(int j=0;j<n;++j)
                      {
                             int c = an[i]&str[j];
                             if(c != 0)
                             sum++;
                      }
                      //cout<<sum<<endl;
                      if(sum%2 !=0 )
                      {
                            cout<<"Yes"<<endl;
                            break;
                      }
             }
             if(i == 31)
             cout<<"No"<<endl;
      }
      return 0;
}