HDU 3404 Switch lights 博弈论 nim积
http://acm.hdu.edu.cn/showproblem.php?pid=3404 题目
http://www.doc88.com/p-5098170314707.html 论文 nim积在22页附近
http://blog.csdn.net/kele52he/article/details/77099890 抄的代码的来源
根据论文相关部分和自己的理解的介绍。(nim积其实没什么卵用,学这种毒瘤的都有猫病。)
nim和其实就是异或,想一下之前sg函数或者nim游戏结算的时候,是几堆在一起玩的,异或之后就是这几堆游戏的nim和。
想一个二维翻硬币游戏,给出正面朝上的硬币的坐标,每次翻两个同排或同列的硬币,其中坐标较大的翻之前必须正面朝上,没法操作的为输。坐标从0开始。
那么每个位置的sg函数就相当于横纵坐标的nim和,因为每个位置的硬币相当于两个博弈游戏放在一起玩。这个sg函数的计算方式称作x⊕y。
再想另一个规则的二维翻硬币游戏,给出正面朝上的硬币的坐标,每次必须同时翻四个能作为同一个矩形的四个顶点的硬币,其中列数和排数最大的硬币必须为正面朝上,没法操作的为输。坐标从0开始。(这就是hdu3404的题意)
那么这次的sg函数就较为复杂,我们把位置(x,y)的的sg函数称为xⓧy。
ⓧ操作有如下规则。
1.单位元: xⓧ1=1ⓧx=x; 2.交换律: xⓧy=yⓧx;
3.结合律: (xⓧy)ⓧz=(xⓧy)ⓧz; 4.对异或的分配率: (x⊕y)ⓧz=(xⓧz)⊕(yⓧz).
ⓧ的基本运算性质: 对于x,y<2^(2^a)
1.xⓧ( 2^(2^a) ) = ( 2^(2^a) )*x;
2.xⓧy < ( 2^(2^a) );
3.( 2^(2^a) ) ⓧ( 2^(2^a) )=3/2* ( 2^(2^a) ).
根据这些基本性质,我们可以得出一个递归求nim积的板子,这里放上这道题的代码。
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 #include<cmath> 5 #include<iostream> 6 #include<map> 7 #include<ctime> 8 using namespace std; 9 int n; 10 int sg[21][21]={}; 11 int f(int,int); 12 int g(int x,int y){ 13 if(sg[x][y]!=-1)return sg[x][y]; 14 if(!x)return sg[x][y]=1<<y; 15 if(!y)return sg[x][y]=1<<x; 16 int ans=1,k=1,t; 17 int x1=x,y1=y; 18 while(x||y){ 19 t=1<<k; 20 if((x^y)&1){ 21 ans*=t; 22 } 23 x>>=1;y>>=1;k<<=1; 24 } 25 k=1;x=x1;y=y1; 26 while(x||y){ 27 t=1<<k; 28 if((x&y)&1){ 29 ans=f(ans,t/2*3); 30 } 31 x>>=1;y>>=1;k<<=1; 32 }return sg[x1][y1]=ans; 33 } 34 int f(int x,int y){ 35 if(!x||!y)return 0; 36 if(x==1)return y; 37 if(y==1)return x; 38 int ans=0; 39 for(int i=x,a=0;i;i>>=1,a++){ 40 if(!(i&1))continue; 41 for(int j=y,b=0;j;j>>=1,b++){ 42 if(!(j&1))continue; 43 ans^=g(a,b); 44 } 45 }return ans; 46 } 47 int main(){ 48 int T;scanf("%d",&T); 49 memset(sg,-1,sizeof(sg)); 50 while(T-->0){ 51 scanf("%d",&n); 52 int ans=0,x,y; 53 for(int i=1;i<=n;i++){ 54 scanf("%d%d",&x,&y); 55 ans^=f(x,y); 56 } 57 if(ans)printf("Have a try, lxhgww.\n"); 58 else printf("Don't waste your time.\n"); 59 } 60 return 0; 61 }