CCF201803-4-棋局评估

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


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

输入格式

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

输出格式

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

样例输入

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

样例说明

  第一组数据:
  Alice将棋子放在左下角(或右下角)后,可以到达问题描述中的局面,得分为3。
  3为Alice行棋后能到达的局面中得分的最大值。
  第二组数据:


  Bob已经获胜(如图),此局面得分为-(3+1)=-4。
  第三组数据:
  井字棋中若双方都采用最优策略,游戏平局,最终得分为0。

数据规模和约定

  对于所有评测用例,1 ≤ T ≤ 5。

解题思路:

属于博弈下棋

用极大极小搜索可解决,使用α,β剪枝,可以加快搜索,不用的话也可以过,因为棋盘很小。

需要注意的问题

(1)α,β的初值很有讲究,太大会消耗时间,太小会出错,这个α,β必须是临界最大值和最小值,比如这道题最大值是5,就是三个×连起来,⚪两个,则Alice获胜,剩余格子4个,加一就是5,反过来最小值就是-5

(2)评测函数返回值,中如果题里没说则为-1,0,1,先手输,平局,先手赢,本题告诉你计算方法,则按规则进行即可,注意判断一行或一列相等不能使用连等,如a[0][1]==a[0][2]==a[0][3],因为这个错误导致找了很长时间bug,对lmh大神表示感谢,正确应为a[0][1]==a[0][2]&&a[0][1]==a[0][3],

(3)如果棋局已经有人获胜,也就是评价函数返回值不为0,则不继续递归,直接返回评价函数值。这种判定直接返回的语句长方在函数的首部,也就是函数内容的第一行。放别的地方容易出错,总之模板是最简化的,每道题应按照本题进行相应的修改。

代码如下:

#include<stdio.h>
#include<math.h>
#include<iostream>
using namespace std;
int a[3][3];
int dep;
int maxx(int dep,int al,int pe);
int minn(int dep,int al,int pe);
int value()
{
    int s=0;
    for(int i=0; i<3; i++)
    {
        for(int j=0; j<3; j++)
        {
            if(a[i][j]==0)s++;
        }
    }
    if(a[0][1]==a[0][0]&&a[0][2]==a[0][0])
    {
        if(a[0][0]==1)return s+1;
        else if(a[0][0]==2)return -(s+1);
    }
    if(a[1][1]==a[1][0]&&a[1][2]==a[1][0])
    {
        if(a[1][0]==1)return s+1;
        else if(a[1][0]==2)return -(s+1);
    }
    if(a[2][1]==a[2][0]&&a[2][2]==a[2][0])
    {
        if(a[2][0]==1)return s+1;
        else if(a[2][0]==2)return -(s+1);
    }
    if(a[0][0]==a[1][0]&&a[0][0]==a[2][0])
    {
        if(a[0][0]==1)return s+1;
        else if(a[0][0]==2)return -(s+1);
    }
    if(a[0][1]==a[1][1]&&a[0][1]==a[2][1])
    {
        if(a[0][1]==1)return s+1;
        else if(a[0][1]==2)return -(s+1);
    }
    if(a[0][2]==a[1][2]&&a[0][2]==a[2][2])
    {
        if(a[0][2]==1)return s+1;
        else if(a[0][2]==2)return -(s+1);
    }
    if(a[0][0]==a[1][1]&&a[0][0]==a[2][2])
    {
        if(a[0][0]==1)return s+1;
        else if(a[0][0]==2)return -(s+1);
    }
    if(a[0][2]==a[1][1]&&a[1][1]==a[2][0])
    {
        if(a[0][2]==1)return s+1;
        else if(a[0][2]==2)return -(s+1);
    }

    return 0;

}
int minn(int dep,int al,int pe)
{
    if(value()!=0)return value();
    int ar=al,pi=pe;
    if(dep<=0)
    {
        return value();
    }
    for(int i=0; i<3; i++)
    {
        for(int j=0; j<3; j++)
        {
            if(a[i][j]==0)
            {
                a[i][j]=2;

                int val=maxx(dep-1,ar,pi);
                a[i][j]=0;
                pi=min(pi,val);
                if(ar>=pi)return pi;
            }
        }
    }
    return pi;
}
int maxx(int dep,int al,int pe)
{
    if(value()!=0)return value();
    int ar=al,pi=pe;
    if(dep<=0)
    {
        return value();

    }
    for(int i=0; i<3; i++)
    {
        for(int j=0; j<3; j++)
        {
            if(a[i][j]==0)
            {
                a[i][j]=1;


                int val=minn(dep-1,ar,pi);
                a[i][j]=0;
                ar=max(ar,val);
                if(ar>=pi)return ar;
            }
        }
    }
    return ar;
}
int main()
{
    int n;
    scanf("%d",&n);
    while(n--)
    {
        int o=0;
        for(int i=0; i<3; i++)
        {
            for(int j=0; j<3; j++)
            {
                scanf("%d",&a[i][j]);
                if(a[i][j]==0)o++;
            }
        }
        int u=maxx(o,-10,10);
        printf("%d\n",u);
    }

}

 

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值