POJ_2311

    这个题目一开始让我头疼死了,关键就在于没办法把自己前面学的SG函数的一些理论应用进去,说道这推荐一个讲SG函数入门知识的博客:http://www.cnblogs.com/Knuth/archive/2009/09/05/1561007.html,博主有几篇和博弈相关的文章,感觉写的还是很棒的。

    就我而言,首先遇到的第一个问题就是抽象不出博主在《Game theory初步》(在前面那个网址的基础上向前找博主的前几篇博客就可以找到)这篇中讲的ICG(公平组合游戏)模型,因为如果纸片出现了1*1就结束了,而其他的纸片却不一定都是1*1的纸片,如果我们把一个纸片抽象成一个游戏图上的点,也就是说有些点还可以决策,但游戏却已经结束了,这明显和ICG的定义是违背的。

    后来看了dicuss之后恍然大悟,对于任意一个选手来讲,肯定不会优先生成1*n或者n*1的纸片,因为下一轮对方就可以取胜,因此,如果这样不断的剪下去的话,最终肯定有一个局面都是3*2、2*3、2*2的纸片,实际上这时就可以断定输赢了,同时如果我们把3*2、2*3、2*2都看成终止状态的话,就巧妙避免了上面所提到的问题,因为这样游戏终止时不会有尚未决策的点。

    单单解决完上一个问题还是不够的,接下来我又遇到了另一个问题,就是之前只做过一些裸的类似Nim游戏的题目,这些题目很容易将游戏转化成图游戏(即一个或若干个棋子初始时在某些特定的位置,每次将一个棋子通过一条有向边移到另一个位置,当所有棋子都不能移动的时候,游戏就终止了),但这个问题就不一样了,如果我们将一个w*h的纸片看做点(w,h),假如一种切的方式是生成(a,b)和(c,d)两个纸片,那岂不成了当一个棋子在(w,h)这个点的时候,有一种决策是让这个棋子分裂成两个棋子,其中一个移到(a,b),另一个移到(c,d)么?这要怎么搞呢?我瞬间就晕了……

    结果今天早晨起床的时候却突然悟到了一些,实际上也主要是这时思考的时候就把游戏和的概念联系起来一起思考了。对于位于(w,h)这个位置的棋子而言,如果变成两个分别位于(a,b)和(c,d)的棋子,这两个棋子是不能各自看成(w,h)的后继的,因为后继之间应该是相互独立的,即既可以通过决策到达其中一个位置,也可以通过决策到达另一个位置,所以我们要将这两个棋子合起来考虑,共同看成是(w,h)的一个后继。同时结合游戏和的一些知识,(a,b)以及(c,d)是不是可以看成(w,h)的两个子游戏呢?当然可以,因为后面无论是对(a,b)还是(c,d)进行决策,都合对(w,h)进行决策的规则是一样的。那么对于(a,b)和(c,d)共同看成一个后继来讲,这个后继的SG函数值就应该是SG(a,b)^SG(c,d)。

    至此为止,我们就终于把这个剪纸片问题化归成和一些简单的Nim游戏类似的形式了,其余的求解过程就和一些简单的Nim游戏的求解过程一样了。

#include<stdio.h>
#include<string.h>
#define MAXD 210
int sg[MAXD][MAXD], W, H;
void prepare()
{
int i, j, k;
memset(sg, -1, sizeof(sg));
sg[2][2] = sg[3][2] = sg[2][3] = 0;
}
int dfs(int x, int y)
{
if(sg[x][y] != -1)
return sg[x][y];
int i, j, k, h[200];
memset(h, 0, sizeof(h));
for(i = 2; i <= x - i; i ++)
h[dfs(i, y) ^ dfs(x - i, y)] = 1;
for(i = 2; i <= y - i; i ++)
h[dfs(x, i) ^ dfs(x, y - i)] = 1;
for(i = 0; h[i]; i ++);
return sg[x][y] = sg[y][x] = i;
}
void solve()
{
if(dfs(W, H) == 0)
printf("LOSE\n");
else
printf("WIN\n");
}
int main()
{
prepare();
while(scanf("%d%d", &W, &H) == 2)
{
solve();
}
return 0;
}


posted on 2012-02-26 10:22  Staginner  阅读(924)  评论(2编辑  收藏  举报