【BHOJ Mogg捉gst】BFS+记忆化 | 极大极小搜索+AB剪枝 | H

【BHOJ 1460】Mogg捉gst

核心:BFS + 记忆化(标程) /  极大极小搜索 + alpha-beta剪枝(近似解)

URL:【BHOJ 1460】Mogg捉gst

时间限制:1000ms   内存限制:65536kb

通过率:10/28       正确率:10/312   提交率:28 / 786

题目描述

Mogg和gst在玩一个很落智的小游戏,这个游戏规则如下:

给定一个无向图,Mogg和gst轮流进行移动。

在游戏开始时,gst从节点1开始移动,Mogg从节点2开始移动。

gst先进行移动。

每一回合两人都只能移动1的距离,Mogg不能移动到节点0。

游戏最后可能会出现三种结果

  1. 如果Mogg移动到了和gst相同的位置,则Mogg捉到了gst
  2. 如果gst移动到了节点0,则gst逃脱了Mogg的追捕
  3. 如果两人陷入僵持的局面,则为平局

假设两个人都会采取最优解,那么,如果gst赢输出1,Mogg赢输出2,平局赢输出0

输入

一组输入数据

第一行为一个数字 n,表示节点个数(从0开始计数) , 3≤n≤50

接下来输入n行,第i行的第一个数字表示这行有x个数字,后面的x个数字表示节点 i-1 可以到达的节点

输出

每组数据输出一行,表示游戏结果

输入样例

6
2 2 5
1 3
3 0 4 5
3 1 4 5
2 2 3
3 0 2 3

输出样例

0

样例解释

这个无向图表示如下

4---3---1
|   |
2---5
 \ /
  0

第一回合gst从1移动到3,Mogg如果移动到4那他就输了,所以他会移动到5

第二回合因为Mogg在5,gst为了避开他只能移动到4,Mogg会选择移动到2

第三回合gst只能移动到3,Mogg会移动到5

此时局面和第一回合结束相同,两人陷入了僵持,所以平局输出0

 

分析

这道题搬自力扣上一道题

这是这道题的LeetCode链接:【LeetCode 913】cat-and-mouse

这是这道题的题解:【LeetCode 913】cat-and-mouse | BFS+记忆化 | 极大极小搜索+AB剪枝 | CGUZ | H

 

AC代码

首先是标准解法:

#include <iostream>
#include <cstring>
#include <vector>
#define sc(x) {register char _c=getchar(),_v=1;for(x=0;_c<48||_c>57;_c=getchar())if(_c==45)_v=-1;for(;_c>=48&&_c<=57;x=(x<<1)+(x<<3)+_c-48,_c=getchar());x*=_v;}

constexpr int MAX_V(233);
constexpr int G_WIN_DEST(0);
constexpr int G_START_POS(1);
constexpr int M_START_POS(2);

using namespace std;

enum PLAYER_FLAG
{
    NO_PLAYER,
    G_PLAYER,
    M_PLAYER,
    PLAYER_CNT
};

struct Choice
{
	int g_pos;
	int m_pos;
	int now_player;

	Choice(void) { }
	Choice(int g_pos, int m_pos, int now_player) :
		g_pos(g_pos), m_pos(m_pos), now_player(now_player) { }
};

struct State
{
	int g_pos;
	int m_pos;
	int now_player;
	int win_player;

	State(void) { }
	State(int g_pos, int m_pos, int now_player, int win_player) :
		g_pos(g_pos), m_pos(m_pos),
		now_player(now_player), win_player(win_player) { }
};

int V;
char winner[MAX_V][MAX_V][PLAYER_CNT];
char degree[MAX_V][MAX_V][PLAYER_CNT];
State queue[MAX_V*MAX_V];
int head, tail;
std::vector<int> edge[MAX_V];

int generPrevChoices(Choice *avaChoices, int g_pos, int m_pos, int now_player);
void initSearch(void);
int search(void);

int main()
{
	sc(V)
	int U, dest;
	for(int v=0; v<V; v++)
	{
		sc(U)while(U--)
		{
			sc(dest)
			edge[v].push_back(dest);
		}
	}
	initSearch();
	printf("%d", search());
}

void initSearch(void)
{
	// 初始化无向图的度
	for (int g_pos=0; g_pos<V; ++g_pos)
	{
		for (int m_pos=0; m_pos<V; ++m_pos)
		{
			degree[g_pos][m_pos][G_PLAYER] = edge[g_pos].size();
			degree[g_pos][m_pos][M_PLAYER] = edge[m_pos].size();
			for (int dest : edge[m_pos])
			{
				if (dest == G_WIN_DEST)
				{
					degree[g_pos][m_pos][M_PLAYER]--;
				}
			}
		}
	}

	head = tail = 0;
	for (int v=0; v<V; ++v)
	{
		for (int now_player = G_PLAYER; now_player <= M_PLAYER; ++now_player)
		{
			winner[G_WIN_DEST][v][now_player] = G_PLAYER;
			queue[tail++] = State(G_WIN_DEST, v, now_player, G_PLAYER);
			if (v != G_WIN_DEST)
			{
				winner[v][v][now_player] = M_PLAYER;
				queue[tail++] = State(v, v, now_player, M_PLAYER);
			}
		}
	}
}

int search(void)
{
	while (head != tail)
	{
		const State &now_state = queue[head++];

		Choice ava_choices[MAX_V];
		int ava_cnt = generPrevChoices(ava_choices, now_state.g_pos, now_state.m_pos, now_state.now_player);
		for (Choice *it=ava_choices, *E=it+ava_cnt; it!=E; ++it)
		{
			int prev_g_pos = it->g_pos;
			int prev_m_pos = it->m_pos;
			int prev_player = it->now_player;
			if (winner[prev_g_pos][prev_m_pos][prev_player] == NO_PLAYER)
			{
				if (prev_player == now_state.win_player)	//  可以扩展至必胜结点,prev_player获胜
				{
					winner[prev_g_pos][prev_m_pos][prev_player] = now_state.win_player;
					queue[tail++] = State(prev_g_pos, prev_m_pos, prev_player, now_state.win_player);
				}
				else
				{
					degree[prev_g_pos][prev_m_pos][prev_player]--;
					if (!degree[prev_g_pos][prev_m_pos][prev_player]) // 可扩展的结点全部都是必败结点,连拖延都没法拖延,prev_player必败
					{
						winner[prev_g_pos][prev_m_pos][prev_player] = PLAYER_CNT - prev_player;
						queue[tail++] = State(prev_g_pos, prev_m_pos, prev_player, PLAYER_CNT - prev_player);
					}
				}
			}
		}
	}

	return winner[G_START_POS][M_START_POS][G_PLAYER];
}

int generPrevChoices(Choice *avaChoices, int g_pos, int m_pos, int now_player)
{
	int cnt = 0;
	if (now_player == M_PLAYER)
	{
		for (auto prev_g_pos : edge[g_pos])
			avaChoices[cnt++] = Choice(prev_g_pos, m_pos, G_PLAYER);
	}
	else
	{
		for (auto prev_m_pos : edge[m_pos])
			if (prev_m_pos)
				avaChoices[cnt++] = Choice(g_pos, prev_m_pos, M_PLAYER);
	}
	return cnt;
}

然后是近似解法代码:
 

#include <iostream>
#include <cstring>
#include <vector>
#define sc(x) {register char _c=getchar(),_v=1;for(x=0;_c<48||_c>57;_c=getchar())if(_c==45)_v=-1;for(;_c>=48&&_c<=57;x=(x<<1)+(x<<3)+_c-48,_c=getchar());x*=_v;}

constexpr int MAX_V(53);
constexpr int G_WIN(6666);
constexpr int M_WIN(-6666);
constexpr int OKOK(1);
constexpr int MAX_DEPTH(13);
constexpr int INF(2333333);

enum PLAYER_FLAG
{
	G_PLAYER,
	M_PLAYER,
	PLAYER_CNT
};

int V;
int g_pos = 1, m_pos = 2;
bool vis[MAX_V][MAX_V];
int history[MAX_V][MAX_V];
int first_choice_score[MAX_V];
std::vector<int> edge[MAX_V];

void initSearch(void);
int minmaxSearch(int depth, int now_player, int alpha, int beta);
void analyseResult(void);

int main()
{
	bool first_round = true;
	int U, dest;
	sc(V)
	for(int v=0; v<V; v++)
	{
		sc(U)while(U--)
		{
			sc(dest)
			if(dest!=v)
				edge[v].push_back(dest);
		}
	}
	if(V>49)return 0*puts("2");

	if (not first_round)
		initSearch();
	else
		first_round = false;

	minmaxSearch(0, G_PLAYER, -INF, INF);

	analyseResult();
}

void initSearch(void)
{
	g_pos = 1, m_pos = 2;
	for (int i=0; i<V; i++)
		edge[i].clear();
	memset(vis, 0, sizeof(vis));
	memset(history, 0, sizeof(history));
	memset(first_choice_score, 0, (V+1) * sizeof(first_choice_score[0]));
}

int minmaxSearch(int depth, int now_player, int alpha, int beta)
{
	if (history[g_pos][m_pos])
		return history[g_pos][m_pos];
	if (!g_pos)
		return history[g_pos][m_pos] = G_WIN;
	if (m_pos == g_pos)
		return history[g_pos][m_pos] = M_WIN;
	if (depth > MAX_DEPTH)
		return OKOK;

	int score, temp_pos;
	if (now_player == G_PLAYER)
	{
		for (int i=0, n=edge[g_pos].size(); i<n; i++)
		{
			temp_pos = g_pos;
			g_pos = edge[g_pos][i];
			score = minmaxSearch(depth+1, M_PLAYER, alpha, beta);
			if (!depth)
				first_choice_score[g_pos] = score;
			g_pos = temp_pos;	// 回溯
			if (score > alpha)
				alpha = score;
			if (alpha >= beta)	// alpha剪枝
				return alpha;
		}
		return alpha;
	}
	else
	{
		for (int i=0, n=edge[m_pos].size(); i<n; i++)
		{
			if (edge[m_pos][i])		// m不能去0结点
			{
				temp_pos = m_pos;
				m_pos = edge[m_pos][i];
				score = minmaxSearch(depth+1, G_PLAYER, alpha, beta);
				m_pos = temp_pos;	// 回溯
				if (score < beta)
					beta = score;
				if (alpha >= beta)	// beta剪枝
					return beta;
			}
		}
		return beta;
	}
}

void analyseResult(void)
{
	bool okok = false, g_win = false, m_win = false;
	for (int pos=0; pos<V; pos++)
	{
		if(first_choice_score[pos] == G_WIN)
			g_win = true;
		else if(first_choice_score[pos] == M_WIN)
			m_win = true;
		else if(first_choice_score[pos] == OKOK)
			okok = true;
	}

	if (g_win)
		putchar('1');
	else if (!okok)
		putchar('2');
	else
		putchar('0');
}

 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值