【SPOJ-MGAME1】Game【博弈DP】

题意:

一个n×m的地图,地图上有些地方不能走,有些地方有分值(1/3/5)。两个玩家移动一个棋子,棋子只能向下、右、右下移动。棋子到达一个格子,这个格子的分值属于移动方。当棋子无法移动时,分数高的获胜,如果分数一样,无法移动者输。

有多组询问,每个询问给出一个起点,问先手赢还是后手赢。


这题我能玩一年。

设dp[i][j][0]代表先手从(i, j)出发所能获取的最大分值,设dp[i][j][1]代表后手从(i, j)出发所能获取的最大分值,g[i][j]代表(i, j)格上的分值。

以(i, j)从(i, j + 1)转移来为例,有

dp[i][j][0] = dp[i][j + 1][1] + g[i][j + 1]

dp[i][j][1] = dp[i][j + 1][0]

这样做太蛋疼了,因为最多从3个状态转移,可是不知道按什么标准选取状态。

取先手最大值?WA了。

先手最大值相等再取后手最小值?WA了。


此时,我们将两个状态转移方程做差,设dp[i][j] = dp[i][j][0] - dp[i][j][1]。

得到dp[i][j] = g[i][j + 1] - dp[i][j + 1]。

发现维护dp[i][j]最大值,即维护先手后手得分的差值的最大值,就可以了。


有个小处理,将无法移动的状态的先手dp[i][j][0] = -0.5,即dp[i][j] = -0.5,这样将同分情况化为直接比较得分,更方便点。


注意:

此题有一组数据,用C,S,B代替了H,F,I,注意读入。


#include <cstdio>
#include <algorithm>

using namespace std;

const int maxn = 105, inf = 0x3f3f3f3f;

int n, m, g[maxn][maxn];
double dp[maxn][maxn];

inline int iread() {
	int f = 1, x = 0; char ch = getchar();
	for(; ch < '0' || ch > '9'; ch = getchar()) f = ch == '-' ? -1 : 1;
	for(; ch >= '0' && ch <= '9'; ch = getchar()) x = x * 10 + ch - '0';
	return f * x;
}

inline int cread() {
	char ch = getchar(); for(; ch != '.' && ch != 'H' && ch != 'F' && ch != 'I' && ch != '#' && ch != 'C' && ch != 'S' && ch != 'B'; ch = getchar());
	if(ch == '.') return 0;
	if(ch == 'H' || ch == 'C') return 1;
	if(ch == 'F' || ch == 'S') return 3;
	if(ch == 'I' || ch == 'B') return 5;
	if(ch == '#') return -1;
}

inline bool check(int x, int y) {
	return x <= n && y <= m && g[x][y] >= 0;
}

int main() {
	n = iread(); m = iread();
	for(int i = 1; i <= n; i++) for(int j = 1; j <= m; j++) dp[i][j] = -inf, g[i][j] = cread();

	for(int i = n; i >= 1; i--) for(int j = m; j >= 1; j--) if(g[i][j] >= 0) {
		bool flag = 1;
		if(check(i, j + 1)) {
			flag = 0;
			dp[i][j] = max(dp[i][j], g[i][j + 1] - dp[i][j + 1]);
		}
		if(check(i + 1, j)) {
			flag = 0;
			dp[i][j] = max(dp[i][j], g[i + 1][j] - dp[i + 1][j]);
		}
		if(check(i + 1, j + 1)) {
			flag = 0;
			dp[i][j] = max(dp[i][j], g[i + 1][j + 1] - dp[i + 1][j + 1]);
		}
		if(flag) dp[i][j] = -0.5;
	}

	int T = iread();
	while(T--) {
		int x = iread(), y = iread();
		printf(dp[x][y] > 0.0 ? "HAL\n" : "DAVE\n");
	}

	return 0;
}


你可能感兴趣的:(博弈DP)