题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5754
题意:给你一个n*m大小的棋盘,分别以国际象棋的国王、战车、骑士和皇后的走法从(1,1)走到(n,m),而且只能向右或者向下走,问谁有必胜的策略或者是两者平局。
题解:无论是哪一种移动,都可以注意到,如果从起点到某一个点有必胜的策略,那么再以必胜点作为起点可以走到下一个必胜点,也就是以小见大。
一、对于王的走法,可以注意到3*3大小的棋盘,从(1,1)到(3,3)后手是有必胜的策略的,对于N和M更大的情况,我们把横坐标每隔3、纵坐标每隔3的点都画出来,这些点都是符合后手胜的。(因为无论先手怎么移动,后手都能重新移动到这些格子,直到到了终点)如果初始点不在这些点上,就必然是先手胜。因为先手可以立刻移动到上述的点。
二、对于车的走法,注意到,如果目前的位置距离终点的x和y坐标差相等,一定是后手胜。(因为先手只能向下或者向右走一段路;无论他往哪里走,后手往另一维走相同的步数,依然保持这一样一种状态。)反之,先手必然能走到一处相等的位置,转化为上述问题,所以一定是先手胜。
三、马的走法,在大多数情况下都是平局。在模3域下,某些地方会存在先后手赢。画图找规律。
四、皇后的走法最为复杂,不过其实也就是一个威佐夫博弈。
关于威佐夫博弈:http://baike.baidu.com/link?url=Xf_NMvP2GFcr93BAmCLsTqY6ULiseus-TLg0dgG_1yx1C-bzt0pSQAFOt0jMb6WvGwd9S_xIj6ZCtBIc0ygotbc9IglMjGcwHHD8nsohR-bIf5yNzG35AHD5xbhrhkSi)。
我们可以将问题转化为:“有两堆石子,每次可以在一堆里取任意(非空)颗(相当于是车的走法),或者在两堆里取相同(非空)颗(相当于是象的走法),取到最后一颗石子的人获胜,问先后手谁有必胜策略。”为什么说是威佐夫博弈呢。
其实仔细想一想就会明白这个走法产生的局面(画图更为清楚),先手从(1,1)开始走,那么第一步他可以到达任意一个x=1(或y=1,两者对称)的局面,那么我们看x=2的时候,(2,1)(2,2)是先手在第一步能到达的。然而(2,3)是后手的必胜点。那么根据前面所说我们在从(2,3)作为起点开始推可以知道,从(2,3)开始的第一步是可以到达坐标中带有2或者3的任意点(别忘了(3,2)与(2,3)等价),也就是说后手的下一个必胜点必定是从x=4(或y=4)开始的(而且x=4时必定有后手的必胜点),这里又是为什么呢?很简单,就是因为从(2,3)开始走,所有坐标中带有2或者3的所有局面先手都可以到达,而且先手只能去到坐标中带有4的部分局面。如图(把所有坐标+1即可得到本题局面):
由此我们可以推知,皇后的走法,后手必胜点就是威佐夫博弈中的奇异局势,打表即可(也有必胜点的公式求法:一个奇异局势(ak,bk)为:ak =[k(1+√5)/2],bk= ak + k (k=0,1,2,...n 方括号表示取整函数)
这是本人对威佐夫博弈的一点点理解,还不够深入,欢迎吐槽!。
代码如下:
1 #include <iostream> 2 #include <cstring> 3 #include <cstdio> 4 #include <algorithm> 5 #include <vector> 6 #include <cmath> 7 using namespace std; 8 9 const double phi=(1+sqrt(5.0))/2.0; 10 int t,ty; 11 int n,m; 12 int g[1005][1005]; 13 int f[1005]; 14 int vis[2][2005]; 15 16 int main() 17 { 18 int x,y; 19 for(int i=1;;i++) 20 { 21 x=phi*i; 22 y=x+i; 23 if(y>1000) 24 break; 25 f[x]=y; 26 } 27 scanf("%d",&t); 28 while(t--) 29 { 30 scanf("%d%d%d",&ty,&n,&m); 31 if(ty==1) 32 { 33 if(n%2&&m%2) 34 puts("G"); 35 else 36 puts("B"); 37 continue; 38 } 39 else if(ty==2) 40 { 41 if(n==m) 42 puts("G"); 43 else 44 puts("B"); 45 continue; 46 } 47 else if(ty==3) 48 { 49 if(n==m&&n%3==1) 50 puts("G"); 51 else if(max(n,m)%3==0&&max(n,m)-min(n,m)==1) 52 puts("B"); 53 else 54 puts("D"); 55 } 56 else 57 { 58 n--,m--; 59 if(n>m) swap(n,m); 60 if(f[n]==m) 61 puts("G"); 62 else 63 puts("B"); 64 } 65 } 66 return 0; 67 }