poj 1568:Find the Winning Move 【迭代博弈+搜索+剪枝】
题面省略。。。
Input
The input contains one or more test cases, followed by a line beginning with a dollar sign that signals the end of the file. Each test case begins with a line containing a question mark and is followed by four lines representing the board; formatting is exactly as shown in the example. The characters used in a board description are the period (representing an empty space), lowercase x, and lowercase o. For each test case, output a line containing the (row, column) position of the first forced win for x, or '#####' if there is no forced win. Format the output exactly as shown in the example.
Output
For this problem, the first forced win is determined by board position, not the number of moves required for victory. Search for a forced win by examining positions (0, 0), (0, 1), (0, 2), (0, 3), (1, 0), (1, 1), ..., (3, 2), (3, 3), in that order, and output the first forced win you find. In the second test case below, note that x could win immediately by playing at (0, 3) or (2, 0), but playing at (0, 1) will still ensure victory (although it unnecessarily delays it), and position (0, 1) comes first.
Sample Input
? .... .xo. .ox. .... ? o... .ox. .xxx xooo
?
....
....
....
.... (再加一组特殊样例) $
Sample Output
##### (0,1)
#####
大致题意::
给定一个四乘四的棋盘,面对一个残局;问先手再下一步是否可以达成最终的胜利;若可以则输出按顺序的坐标最小的那个。
解决思路:
(我给起的名字是博弈搜索算法,其实是极大极小搜索算法!)
博弈迭代思想+搜索+剪枝。
思路解释起来很麻烦,简单说就是:枚举先手一步,针对新加的新手一步--然后判断是否后手无论怎么下就注定是死局......
注意:
1、在迭代的过程中,平局也是后手胜,因为题目仅要求先手是否面对的是必赢局!
2、我的博客专栏胡搞里也有相关的可以瞅瞅!http://www.cnblogs.com/zhazhaacmer/category/1137385.html
带注释的题解(第二种check()为超时的判断局面的函数):
1 char mp[4][5]; 2 int check(int x,int y,char mp[4][5]){ //判定新更改的点(x,y)是否会构成胜利的局面,两斜/横/竖 3 if(x==y&&mp[0][0]!='.'&&mp[0][0]==mp[1][1]&&mp[1][1]==mp[2][2]&&mp[2][2]==mp[3][3]) 4 return (mp[0][0]=='x'?0:1); 5 if(x+y==3&&mp[0][3]!='.'&&mp[0][3]==mp[1][2]&&mp[1][2]==mp[2][1]&&mp[2][1]==mp[3][0]) 6 return (mp[0][3]=='x'?0:1); 7 if(mp[x][0]!='.'&&mp[x][0]==mp[x][1]&&mp[x][1]==mp[x][2]&&mp[x][2]==mp[x][3]) 8 return (mp[x][0]=='x'?0:1); 9 if(mp[0][y]!='.'&&mp[0][y]==mp[1][y]&&mp[1][y]==mp[2][y]&&mp[2][y]==mp[3][y]) 10 return (mp[0][y]=='x'?0:1); 11 } 12 /* !!下面这种判断是否可以达到结束的局面的方法,时间复杂度高,毕竟把整个图跑了两遍! 13 int check1(char mp[4][5]){ //三种返回值,0表示前者,1表示后者,-1表示平局 14 for(int i=0;i<=3;i++){ 15 if(mp[i][0]!='.'&&mp[i][0]==mp[i][1]&&mp[i][1]==mp[i][2]&&mp[i][2]==mp[i][3]) 16 return (mp[i][0]=='x'?0:1); 17 } 18 for(int i=0;i<=3;i++){ 19 if(mp[0][i]!='.'&&mp[0][i]==mp[1][i]&&mp[1][i]==mp[2][i]&&mp[2][i]==mp[3][i]) 20 return (mp[0][i]=='x'?0:1); 21 } 22 if(mp[0][0]!='.'&&mp[0][0]==mp[1][1]&&mp[1][1]==mp[2][2]&&mp[2][2]==mp[3][3]) 23 return (mp[0][0]=='x'?0:1); 24 if(mp[0][3]!='.'&&mp[0][3]==mp[1][2]&&mp[1][2]==mp[2][1]&&mp[2][1]==mp[3][0]) 25 return (mp[0][3]=='x'?0:1); 26 27 for(int i=0;i<=3;i++){ 28 for(int j=0;j<=3;j++){ 29 if(mp[i][j]=='.') 30 return -1; 31 } 32 } 33 return 1;//假设整个棋盘全部布满,平局就也是后者赢! 34 } 35 */ 36 int check_whole(){ // //判断全局都是点'.',若是则一定是平局,此种特例不特判必定超时 37 for(int i=0;i<=3;i++){ 38 for(int j=0;j<=3;j++) 39 if(mp[i][j]!='.') 40 return false; 41 } 42 return true; 43 } 44 int ansx,ansy;//记录第一次下的点的x和y坐标,也就是step=0的时候! 45 46 bool dfs(int x,int y,int op,int step){//对于op来说,先手X: 0要求必赢,后手1要求不败即可! 47 char ch = (op==0)?'x':'o'; 48 49 int val=check(x,y,mp); 50 if(val==op) 51 return true; 52 else if(val==!op) 53 return false; 54 55 for(int i=0;i<=3;i++){ 56 for(int j=0;j<=3;j++){ 57 if(mp[i][j]=='.'){ 58 mp[i][j]=ch; 59 if(dfs(i,j,!op,step+1)==false){ 60 mp[i][j]='.'; 61 // printf("****step=%d****%d,%d ch=%c ",step,i,j,(op==0)?'x':'o'); 62 if(step==0){ 63 ansx=i;ansy=j; 64 } 65 return true; 66 } 67 mp[i][j]='.'; 68 } 69 } 70 } 71 72 return false; 73 } 74 75 int main(){ 76 char ch[2]; 77 while(scanf("%s",ch),ch[0]!='$'){ 78 for(int i=0;i<=3;i++) 79 scanf("%s",mp[i]); 80 if(check_whole()==true)//特判一次!全图都为点时,不忽略的话注定超时! 81 printf("##### "); 82 else if(dfs(0,0,0,0)==true) 83 printf("(%d,%d) ",ansx,ansy); 84 else 85 printf("##### "); 86 } 87 88 return 0; 89 }