[九省联考2018]一双木棋
Description
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\n", f[0]);
return 0;
}