2 0
8600
# include <stdio.h> int n ; int graph[1010][1010] ; int dfs (int x, int y) { int xx, yy, i, ans ; int tab[4][2] = {-1,0,1,0,0,-1,0,1} ; for (i = 0 ; i < 4 ; i++) { xx = x + tab[i][0] ; yy = y + tab[i][1] ; if (xx < 0 || xx >= n) continue ; if (yy < 0 || yy >= n) continue ; if (graph[xx][yy]) continue ; graph[xx][yy] = 1 ; ans = dfs (xx, yy) ; graph[xx][yy] = 0 ; if (ans == 0) return 1 ; } return 0 ; } int main () { graph[0][0] = 1 ; for (n = 1 ; n <= 8 ; n++) printf ("%d, %d\n", n, dfs (0,0)) ; }
S表示起点。
如果n为偶数,那么所有格子可以被2*1的砖块覆盖掉。
这样先手每次都移动到当前1*2的另外一块。先手必赢。
如果n为奇数。除了起始那个点,其余点都可以被覆盖。
所以后手赢。
定义记号(1,1)表示x坐标为奇,有坐标为奇,(0,1)为x坐标为偶,y坐标为奇 ,(0,0)(1,0)类推。
先说明一下结论:
n是奇数,后手胜,n是偶数,先手胜。
以n为奇数为例,说明一下制胜策略。
那么5*5棋盘为例,可以表示为:
(1,1) (1,0) (1,1) (1,0) (1,1)
(0,1) (0,0) (0,1) (0,0) (0,1)
(1,1) (1,0) (1,1) (1,0) (1,1)
(0,1) (0,0) (0,1) (0,0) (0,1)
(1,1) (1,0) (1,1) (1,0) (1,1)
先手开始(1,1),走了一步之后,必为(1,0)或(0,1);
后手只需移动到(1,1)或(0,0)即可。
一共25个格子,起初(1,1)去掉一个。还有24个,所以对称点和不对称点各有12个。先手都是不对称点,后手都是对称点。
那么刚好应该后手放最后一个。
说道这,有个地方,聪明人可能看出来。有可能没到最后,后手就没办法走了。那么我们假设一下吧。
的确有这个可能 比如2-3-8-9-10-5-4 ,先手2,8,10,4,这样的确不行。(写到这里,卡了我20来分钟,发现以前的想法是错的,还好我又想出个办法如下:)
10是先手选的,那么5是后手选的,10后手不能变,但是后手可以不选择5,改选别的。这样子,会有两个永远都取不到。不影响奇偶性,前面的推断依然可用。
这样子,我们一开始玩。出现第一个不行的话,就选另一个,而且,这样一定会舍弃偶数个格子。
严格来说,应该这样。设最后一个是先手点,那么前一个是后手点,这个后手点如果能改变则改变,如果不能改,就往前推。这样子,一改就是成对的点。要么最后取不到,要么换个方向取,不管怎么样,通过这种更改的方法,对每一种先手方案,一定能够造出后手必胜策略出来的。(能改变的后手点总是取得到的,这是因为根据图的点类型的对称性)
想想这个思想就是试验——出错——改进并且一定能改好。这样一步一步,结果能构造出。
如果n是偶数的话,对称点和不对称点个数差一,不对称多一个,是先手点的。思路同上。
/************************************** *************************************** * Author:Tree * *From :http://blog.csdn.net/lttree * * Title : Play a game * *Source: hdu 1564 * * Hint : 规律博弈 * *************************************** **************************************/ #include <iostream> using namespace std; int main() { int n; while( cin>>n && n) if( n&1 ) cout<<"ailyanlu"<<endl; else cout<<"8600"<<endl; return 0; }