题目大意
给定一个\(n \times m\)的棋盘,每个格子有两个权值\(a,b\),A想最大化分差B最小化,A先出手。某格子可以下棋当且仅当其上方全部被下满并且左边全部被下满,请问下满整个棋盘的分数是多少?
\(n,m \leq 10\)
思路
模拟下的过程,发现它图形永远都是要给上三角形。用二进制来维护它的轮廓进行dp。
设\(f[S]\)表示按照规则的当前得分,转移的时候找“拐角”(01/10),在拐角处进行操作维护。
代码
#include <bits/stdc++.h>
using namespace std;
#define ll long long
const int N = 11;
const int INF = 0x3f3f3f3f;
int n,m;
int a[N][N];
int b[N][N];
int f[1 << (2 * N - 1)];
bool vis[1 << (2 * N - 1)];
int dp (int sta,int x) {
if(sta == ((1 << n) - 1) << m) return 0;
if(vis[sta]) return f[sta];
vis[sta] = 1;
f[sta] = x ? -INF : INF;
int xx = n;
int yy = 0;
for(int i = 0;i < (n + m - 1);i ++) {
if((sta >> i) & 1) {
-- xx;
}else ++ yy;
if(((sta >> i) & 3) != 1) continue;//不是拐角
int new_sta = sta ^ (3 << i);
if(x) {
f[sta] = max(f[sta],dp(new_sta,x ^ 1) + a[xx][yy]);
}
else f[sta] = min(f[sta],dp(new_sta,x ^ 1) - b[xx][yy]);
}
return f[sta];
}
int main () {
cin >> n >> m;
for(int i = 0;i < n; i ++) {
for(int j = 0;j < m; j ++) {
cin >> a[i][j];
}
}
for(int i = 0;i < n; i ++) {
for(int j = 0;j < m; j ++) {
cin >> b[i][j];
}
}
int sta = (1 << n) - 1;
cout << dp(sta,1) << endl;
return 0;
}