CCF 201803-4 棋局评估

解题思路

首先分析,题目给出的是一个棋盘,让我们计算轮到1下时,当前局面的得分是多少。两人都以最优策略行棋。显然这是一个对抗搜索的问题,需要用到极大极小算法,由于数据量较小,不需要用到alpha-beta剪枝。

  1. 构思设计算法。每一步都需要有一个判断是否已经结束的函数,和一个判断当前得分的函数,先设计好这两个基础函数,然后设计总的搜索算法。
  2. 搜索。
  • 根据极大极小搜索算法不断深入,我们能判断出每一个终局的得分,并自底向上递推每个非终局的分数。MAX用来储存他所有子局面分数的最大值(因为1会选择最优策略行棋),MIN用来储存所有子局面分数的最小值(因为2会选择一个对1最不利的操作)。
  • 详细算法步骤见代码如下。
#include
#include
#include
#include
using namespace std;

int T;
int c[4][4]={0};

bool judge(int x){ //判断当前情况是否第x人已获胜 
	for(int i=1;i<=3;i++){ 
		if(c[i][1]==c[i][2]&&c[i][1]==c[i][3]&&c[i][1]==x) return true;
	}
	for(int j=1;j<=3;j++){
		if(c[1][j]==c[2][j]&&c[1][j]==c[3][j]&&c[1][j]==x) return true;
	}
	if(c[1][1]==c[2][2]&&c[1][1]==c[3][3]&&c[1][1]==x) return true;
	if(c[1][3]==c[2][2]&&c[1][3]==c[3][1]&&c[1][3]==x) return true;
	return false;
}

int goal(int x){ //判断第x个人赢的情况下,他的得分是多少 
	int ans=1;
	for(int i=1;i<=3;i++){
		for(int j=1;j<=3;j++)
			if(c[i][j]==0) ans++;
	}
	if(x==1) return ans;
	return -ans;
}

int dfs(int x){
	if(goal(1)==1) return 0; //若已无子可下,平局 
	int MAX=-1<<30; 
	int MIN=1<<30; 
	for(int i=1;i<=3;i++){
		for(int j=1;j<=3;j++){
			if(c[i][j]) continue;
			c[i][j]=x;
			if(judge(x)){ //若第x人在该局已经获胜
				if(x==1) MAX=max(MAX,goal(1)); //将当前局面和已知的最优解比较,取较大值
				else MIN=min(MIN,goal(2));
			}
			else{ //若第x人该步未获胜 
				if(x==1) MAX=max(MAX,dfs(2)); //将未来局面和已知的最优解比较,取较大值
				else MIN=min(MIN,dfs(1));
			}
			c[i][j]=0; //回溯 
		}	
	}
	if(x==1) return MAX; //若这一步是x走的,返回他在最优情况下的得分 
	return MIN;
}

int main(){
	scanf("%d",&T);
	while(T--){
		for(int i=1;i<=3;i++){
			for(int j=1;j<=3;j++){
				scanf("%d",&c[i][j]);	
			}
		}
		if(judge(1)) printf("%d\n",goal(1)); //若该局面1已获胜 
		else if(judge(2)) printf("%d\n",goal(2)); //若该局面2已获胜 
		else printf("%d\n",dfs(1)); //第1人以最优步骤下棋 
	}
	return 0;
}

你可能感兴趣的:(CCF,CSP历年真题)