棋局评估

描述:

 

NPU_LL和NPU_ZTS正在玩井字棋游戏。
  井字棋游戏的规则很简单:两人轮流往3*3的棋盘中放棋子,NPU_LL放的是“X”, NPU_ZTS放的是“O”, NPU_LL执先。当同一种棋子占据一行、一列或一条对角线的三个格子时,游戏结束,该种棋子的持有者获胜。当棋盘被填满的时候,游戏结束,双方平手。
  NPU_LL设计了一种对棋局评分的方法:
  - 对于NPU_LL已经获胜的局面,评估得分为(棋盘上的空格子数+1);
  - 对于NPU_ZTS已经获胜的局面,评估得分为 -(棋盘上的空格子数+1);
  - 对于平局的局面,评估得分为0;

 
-------
|X|O|X|
-------
|O|X|O|
-------
|X|   |   |
-------
例如上图中的局面,NPU_LL已经获胜,同时棋盘上有2个空格,所以局面得分为2+1=3。
  由于NPU_LL并不喜欢计算,所以他请教擅长编程的你,如果两人都以最优策略行棋,那么当前局面的最终得分会是多少?

输入:

 

 输入的第一行包含一个正整数T,表示数据的组数。
  每组数据输入有3行,每行有3个整数,用空格分隔,分别表示棋盘每个格子的状态。0表示格子为空,1表示格子中为“X”,2表示格子中为“O”。保证不会出现其他状态。
  保证输入的局面合法。(即保证输入的局面可以通过行棋到达,且保证没有双方同时获胜的情况)
  保证输入的局面轮到NPU_LL行棋。

输出:

 

对于每组数据,输出一行一个整数,表示当前局面的得分。

输入样例:

 

3
1 2 1
2 1 2
0 0 0
2 1 1
0 2 1
0 0 2
0 0 0
0 0 0
0 0 0

输出样例:

 

3
-4
0

 

思路:

    如果A要赢,那B在下最后一步的之前,A应该有两种方案凑够3个,这样,B只能堵住一个,A就赢了。

    每次搜索前,判断一下刚走完的那个人是否会有两种方案凑够3个棋,有就可以更新答案。

    注意:没走或只走了一步有可能就结束了,而之前并没有任何一方出现两种赢的方案。

    一些剪枝注释在程代码里了。

  1 #include<cstdio>
  2 #include<cstring>
  3 #include<algorithm>
  4 #include<iostream>
  5 #include<cmath>
  6 #include<cstdlib>
  7 
  8 using namespace std;
  9 
 10 int T,f,tem,iss,bla;
 11 char qi[4][4];
 12 
 13 int judge(int te)//判断当前的人是否出现了两种可赢的方案 
 14 {
 15     char t=te+'0';
 16     int cnt[4][4];
 17     memset(cnt,0,sizeof(cnt));
 18     for(int i=1;i<=3;i++)
 19     {
 20         if(qi[i][1]==qi[i][2]&&qi[i][1]==t&&qi[i][3]=='0')cnt[i][3]++;
 21         if(qi[i][3]==qi[i][2]&&qi[i][2]==t&&qi[i][1]=='0')cnt[i][1]++;
 22         if(qi[i][1]==qi[i][3]&&qi[i][1]==t&&qi[i][2]=='0')cnt[i][2]++;
 23     }
 24     for(int i=1;i<=3;i++)
 25     {
 26         if(qi[1][i]==qi[2][i]&&qi[2][i]==t&&qi[3][i]=='0')cnt[3][i]++;
 27         if(qi[3][i]==qi[2][i]&&qi[2][i]==t&&qi[1][i]=='0')cnt[1][i]++;
 28         if(qi[1][i]==qi[3][i]&&qi[1][i]==t&&qi[2][i]=='0')cnt[2][i]++;
 29     }
 30     if(qi[1][1]==qi[2][2]&&qi[2][2]==t&&qi[3][3]=='0')cnt[3][3]++;
 31     if(qi[3][3]==qi[2][2]&&qi[2][2]==t&&qi[1][1]=='0')cnt[1][1]++;
 32     if(qi[3][3]==qi[1][1]&&qi[1][1]==t&&qi[2][2]=='0')cnt[2][2]++;
 33     if(qi[1][3]==qi[3][1]&&qi[3][1]==t&&qi[2][2]=='0')cnt[2][2]++;
 34     if(qi[1][3]==qi[2][2]&&qi[2][2]==t&&qi[3][1]=='0')cnt[3][1]++;
 35     if(qi[3][1]==qi[2][2]&&qi[2][2]==t&&qi[1][3]=='0')cnt[1][3]++;
 36     int e=0;
 37     for(int i=1;i<=3;i++)
 38       for(int j=1;j<=3;j++)
 39       {
 40            // printf("%d    ",cnt[i][j]); 
 41           if(cnt[i][j])e++;
 42       }    
 43     return e;
 44 }
 45 
 46 int che()//判断当前棋局是否已经有三个连着的 
 47 {
 48     for(int i=1;i<=3;i++)
 49     {
 50         char ch=qi[i][1];
 51         int f=1;
 52         for(int j=2;j<=3;j++)
 53         {
 54             if(ch!=qi[i][j])f=0;
 55         }
 56         if(f)return ch-'0';
 57     }
 58     for(int i=1;i<=3;i++)
 59     {
 60         char ch=qi[1][i];
 61         int f=1;
 62         for(int j=2;j<=3;j++)
 63         {
 64             if(ch!=qi[j][i])f=0;
 65         }
 66         if(f)return ch-'0';
 67     }
 68     if(qi[1][1]==qi[2][2]&&qi[2][2]==qi[3][3])return qi[2][2]-'0';
 69     if(qi[1][3]==qi[2][2]&&qi[2][2]==qi[3][1])return qi[2][2]-'0';
 70     return 0;
 71 } 
 72 
 73 int work(int bu)
 74 {
 75     if(bu-iss<=1&&bu>=5)//直接赢的特殊情况:在走一步以内有5个以上的子才可能直接出现能赢的局面。 
 76     {
 77         if(che()==1)
 78         {
 79             return 9-bu+1;
 80         }
 81         if(che()==2)
 82         {
 83             return -(9-bu+1);
 84         }
 85     }
 86     if(bu>=4)//剪枝:当棋子数超过4才会出现两种能赢的方案,这时再判断。 
 87     {
 88         int x=judge(1),y=judge(2);
 89         if(y>=2&&(!(bu&1)))
 90         {
 91             return bu-8;
 92         }
 93         if(x>=2&&(bu&1))
 94         {
 95             return 8-bu;
 96         }
 97     }
 98     if(bu==7)return 0;//剪枝:如果当前已经有7颗棋还没有找到就一定是平局 
 99     for(int i=1;i<=3;i++)//枚举每一种走法 
100     {
101         for(int j=1;j<=3;j++)
102         {
103             if(qi[i][j]=='0')
104             {
105                 if(bu&1)
106                 {
107                     qi[i][j]='2';
108                     int tt=work(bu+1);
109                     if(abs(tem)<abs(tt))
110                     {
111                         tem=tt;
112                     }
113                     qi[i][j]='0';
114                 }
115                 else
116                 {
117                     qi[i][j]='1';
118                     int tt=work(bu+1);
119                     if(abs(tt)>abs(tem))
120                     {
121                         tem=tt;
122                     }
123                     qi[i][j]='0';
124                 }
125             }
126         }
127     }
128     return tem;
129 }
130 
131 int main()
132 {
133     scanf("%d",&T);
134     for(int I=1;I<=T;I++)
135     {
136         bla=0;iss=0;tem=0;
137         memset(qi,0,sizeof(qi));
138         for(int i=1;i<=3;i++)
139         {
140             for(int j=1;j<=3;j++)
141             {
142                 char ch=getchar();
143                 while((ch!='1')&&(ch!='2')&&(ch!='0'))ch=getchar();
144                 qi[i][j]=ch;
145                 if(ch=='0')bla++;
146                 else iss++;
147             }
148         }
149         if(iss<=1)//剪枝:一定是平局 
150         {
151             printf("0\n");
152             continue;
153         }
154         int ans=work(iss);
155         printf("%d\n",ans);
156     }
157     return 0;
158 }

 

 

posted @ 2018-10-22 21:52  liqgnonqfu  阅读(278)  评论(0编辑  收藏  举报