HOJ2838 Nim or not Nim?

博弈-(取走-分割游戏)
题目大意:Alice和Bob轮流取N堆石子,每堆S[i]个,Alice先,每一次可以从任意一堆中拿走任意个石子,也可以将一堆石子分为两个小堆。先拿完者获胜。(1 ≤ N ≤ 10^6, 1 ≤ S[i] ≤ 2^31 - 1)
算法分析:

可以看到S[i]的值可能非常大,如果计算每一堆的sg值是不现实的,所以需要我们找规律来计算给定的石堆的sg

找了半天规律,但是WA了,说明规律不对。

此题为博弈中的—取走-分割游戏这种游戏允许取走某些东西,然后将原来的一个游戏分成若干个相同的游戏)

1Lasker's Nim游戏:每一轮允许两会中操作之一:①、从一堆石子中取走任意多个,②、将一堆数量不少于2的石子分成都不为空的两堆。

分析:很明显:sg(0) = 0,sg(1) = 1。

状态2的后继有:0,1和(1,1),他们的SG值分别为0,1,0,所以sg(2) =2。

状态3的后继有:0、1、2、(1,2),他们的SG值分别为0、1、2、3,所以sg(3) = 4。

状态4的后继有:0、1、2、3、(1,3)和(2,2),他们的SG值分别为0,1,2,4,5,0,所以sg(4) = 3.

再推一些,推测得到:对于所有的k >= 0,有 sg( 4k+1 ) = 4k+1; sg( 4k+2 ) = 4k+2; sg( 4k+3 ) = 4k+4; sg( 4k+4 ) = 4k+3。

假设游戏初始时有3堆,分别有2、5和7颗石子。三堆的SG函数值分别为2、5、8,他们的Nim和等于15.所以要走到P状态,就要使得第三堆的SG值变成7,可以将第三对按1和6分成两堆。

代码如下:

#include <iostream> using namespace std; int main() { int t,n,a; cin >> t; while( t-- && cin >> n ) { int s = 0 ; for( int i = 0; i < n; i++ ) { cin >> a; if( a % 4 == 3 ) s ^= (a+1); else if( a % 4 == 0) s ^= (a-1); else s ^= a; } if( s == 0 ) cout << "Bob\n"; else cout << "Alice\n"; } return 0; }

//得到SG值,找出SG值的规律。 /*sg[4k]=4k-1   sg[4k+1]=4k+1    sg[4k+2]=4k+2    sg[4k+3]=4k+1  #include <iostream>  using namespace std; #include<string.h>  const int MAX = 1005;  int sg[MAX];  bool vst[MAX];  void take_part(int n)  {      for(int i = 1; i <= n / 2; i++)      {          int yihuo = 0;          yihuo ^= sg[i] ^ sg[n - i];          vst[yihuo] = true;      }  }  void get_sg()  {      memset(sg, 0, sizeof(sg));      for(int i = 0; i < MAX; i++)      {          memset(vst, false, sizeof(vst));          int j = 0;          while(j++ < i)          {              vst[sg[j]] = true;          }          take_part(i);          for(int j = 0; j < MAX; j++)          {              if(!vst[j])              {                  sg[i] = j;                  break;              }          }      }      return ;  }  void view_arr(int a[], int n)  {      for(int i = 0; i < n; i++)      {          cout << "sg[" << i << "]" << ": " << a[i] << endl;      }      return ;  }  int main(void)  {      int cas;      get_sg();      view_arr(sg, 30);      return 0;  } */

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值