[九省联考2018]一双木棋

Description

Luogu4363
BZOJ5248

Solution

可以发现,先手要最大化分差,后手要最小化分差,数据范围又很小,不像找规律,所以应该用min-max搜索。然后,棋子其实是左上方的一块区域,所以可以hash一下轮廓线,记忆化搜索就行了。

Code

#include <cstdio>
#include <map>
#define base (m+1)

typedef unsigned long long LL;
const int N = 11;
const int INF = 0x3f3f3f3f;

int ln[N], n, m;
std::map<LL, int> f;
int B[N][N], A[N][N];

bool is_B() { int p=0; for (int i = 1; i <= n; ++i) p += ln[i]; return p&1; }
LL hash() {
	LL st=0;
	for (int i = 1; i <= n; ++i) st = st * base + ln[i];
	return st;
}
void unhash(LL st) {
	for (int i = n; i; --i) ln[i] = st % base, st /= base;
}
int dfs(LL st) { // 表示在st状态下双方均执行最优策略的答案 
	if (f.count(st)) return f[st];
	unhash(st);
	bool b = is_B(); int ret = b?INF:-INF;
	for (int i = 1; i <= n; ++i) if (ln[i-1] > ln[i]) { 
		ln[i]++;
		LL nxt = hash();
		ret = b?std::min(ret, dfs(nxt) - B[i][ln[i]]):std::max(ret, dfs(nxt) + A[i][ln[i]]);
		ln[i]--;
	}
	return f[st] = ret;
}

int main() {
	scanf("%d%d", &n, &m);
	ln[0] = m;
	for (int i = 1; i <= n; ++i) {
		for (int j = 1; j <= m; ++j) {
			scanf("%d", &A[i][j]);
		}
	}
	for (int i = 1; i <= n; ++i) {
		for (int j = 1; j <= m; ++j) {
			scanf("%d", &B[i][j]);
		}
	} 
	for (int i = 1; i <= n; ++i) ln[i] = m;
	f[hash()] = 0;
	for (int i = 1; i <= n; ++i) ln[i] = 0;
	dfs(0);
	printf("%d
", f[0]);
	return 0;
}
原文地址:https://www.cnblogs.com/wyxwyx/p/jslk2018chess.html