POJ 1143 Number Game

这里给出了完整的题目描述和解题方法,赞之,以下内容为部分它的内容加上自己的理解。

题目大意:Christine和Matt玩一个游戏.游戏的规则如下:一开始有一列数字(2~20),有的被列出,有的没有列出.从Christine开始,两人轮流划去一个已被列出的数字.每划去一个数字,其他的所有的能被不在这个数列的数字的两两整线性表示的数也自动从数列中退出.到最后,轮到某人而此时数列中没有元素时,这个人就输了.
规定:winning move能将对手置于losing position的决策;而losing position不存在任何winning move的序列.

解题大体思路,定义输入N(N<=20)个数字为一个状态,扫描这些输入的数字,假设选择数字num(扫描到num)后,如果当前状态有数字可选 并且 选择num后搜索剩下的数字组成的状态为没有数字可选,则选择的数字num为winning move.

N(N<=20)个数字组成的状态,想办法寻找能对应这个状态的hash函数,于是可以有一个20位的数(unsigned int 的低20位)表示,这位为1表示这位在输入数字中,为0表示不在输入数字中。则可定义一个数组record表示状态(有数字可选、没有数字可选、不确定),数组大小为(1<<20),数组的每一个下标是一个状态(表示一组输入),下标对应的值为状态。

巧妙的是这个数组在每次测试时候不用重新初始化,上一次的结果对这次的测试是有用的。也就是本次测试数字如果为2 5,第二次还是2 5,那程序可以直接得出结果了。原因是2 5对应的二进制为0101 0000 0000 0000 0000,(倒过来),是数组record的下标,读取值就可以知道是不是winning move。程序边运行出结果边填补record的所有内容,所有状态都测试完成后所有结果就都存在record里面了。

链接不保证有效性,那这里贴上代码:

   1: #include <iostream>
   2: #include <memory.h>
   3: using namespace std;
   4: const int NUM_SIZE = 21;
   5: int record[1<<20];
   6:  
   7: inline unsigned int myMap(bool s[])
   8: {
   9:     unsigned int index=0;
  10:     for (int i=2; i<NUM_SIZE; i++)
  11:     {
  12:         if (s[i])    index |= 1;
  13:         index <<= 1;
  14:     }
  15:     return (index>>1);
  16: }
  17:  
  18: bool dfsNext(bool s[], const int pos)
  19: {
  20:     bool curr[NUM_SIZE];
  21:     memcpy(curr, s, NUM_SIZE);
  22:     curr[pos] = false;
  23:     for (int i=2; i+pos<NUM_SIZE; i++)
  24:     {
  25:         if (!curr[i])
  26:             curr[i+pos] = false;
  27:     }
  28:     unsigned index = myMap(curr);
  29:     if (record[index]>0)    
  30:         return true;
  31:     else if (record[index]<0)
  32:         return false;
  33:     
  34:     for (int i=2; i<NUM_SIZE; i++)
  35:     {
  36:         if (curr[i] && (!dfsNext(curr, i)))
  37:         {
  38:             record[index] = 1;
  39:             return true;
  40:         }
  41:     }
  42:     record[index] = -1;
  43:     return false;
  44: }
  45: int main()
  46: {
  47:     int n=0;
  48:     bool source[NUM_SIZE];
  49:     int cases = 1;
  50:     while (cin>>n && n!=0)
  51:     {
  52:         memset(source, false, sizeof(source));
  53:         for (int i=0; i<n; i++)
  54:         {
  55:             int num=0;
  56:             cin >> num;
  57:             source[num] = true;
  58:         }
  59:         unsigned int index=myMap(source);
  60:         int count=0;
  61:         int res[NUM_SIZE];
  62:         memset(res, 0, sizeof(res));
  63:         for (int i=2; i<NUM_SIZE; i++)
  64:         {
  65:             if (source[i] && (!dfsNext(source, i)))
  66:                 res[count++] = i;
  67:         }
  68:         cout << "Test Case #" << cases++ << endl;
  69:         if (count==0)
  70:         {
  71:             record[index] = -1;
  72:             cout << "There's no winning move." << endl;
  73:         }
  74:         else if (count>0)
  75:         {
  76:             cout << "The winning moves are:";
  77:             for (int i=0; i<count; i++)
  78:                 cout << ' ' << res[i];
  79:             cout << endl;
  80:         }
  81:         cout << endl;
  82:     }
  83:     return 0;
  84: }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值