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.
");
58         else printf("Don't waste your time.
");
59     }
60     return 0;
61 }
View Code
原文地址:https://www.cnblogs.com/137shoebills/p/8337690.html