「九省联考 2018」一双木棋
「九省联考 2018」一双木棋
题目描述
菲菲和牛牛在一块 \(n\) 行 \(m\) 列的棋盘上下棋,菲菲执黑棋先手,牛牛执白棋后手。
棋局开始时,棋盘上没有任何棋子,两人轮流在格子上落子,直到填满棋盘时结束。落子的规则
是:一个格子可以落子当且仅当这个格子内没有棋子且这个格子的左侧及上方的所有格子内都有棋
子。
棋盘的每个格子上,都写有两个非负整数,从上到下第 \(i\) 行中从左到右第 \(j\) 列的格子上的两个整数
记作 \(A_{i,j}\),\(B_{i,j}\)。在游戏结束后,菲菲和牛牛会分别计算自己的得分:菲菲的得分是所有有黑棋的格子上的 \(A_{i,j}\) 之和,牛牛的得分是所有有白棋的格子上的 \(B_{i,j}\) 的和。
菲菲和牛牛都希望,自己的得分减去对方的得分得到的结果最大。现在他们想知道,在给定的棋盘上,如果双方都采用最优策略且知道对方会采用最优策略,那么,最终的结果如何。
\(1 \leq n, m \leq 10\)
### 解题思路 : 考虑要求 $Sum_A - Sum_B$ 的结果,本质上是对 $A$ 取得每一个格子 $i,j$ ,$\sum{A_{i,j}} + B_{i,j} - \sum B$
那么 \(A\) 就要最大化其取的每个格子的贡献之和,当中每个格子对答案的贡献是 \(A_{i,j} - B_{i,j}\)
而 \(B\) 要阻止 \(A\) ,使得其取得的贡献尽可能小, 设当前状态为 \(maxk\) 下一个状态为 \(next\) 当且先手为 \(A/B\)
那么有 \(f[mask][A] = \max(f[next][B]) + A_{now} - B_{now}, f[mask][B] = \min(f[next][A])\)
考虑如何表示状态,观察发现任意时刻棋盘每列的最后一个棋子的位置是递减的.
所以状态数等价于从第一行任意一点走到最后一行任意一点,只能向左和向下走到方案数
总状态数不超过 \(C_{20}^{10}\) 直接大力 \(hash\) 一下每一列最右边的点的位置,\(map\) 记录即可
/*program by mangoyang*/
#include<bits/stdc++.h>
#define inf (0x7f7f7f7f)
#define Max(a, b) ((a) > (b) ? (a) : (b))
#define Min(a, b) ((a) < (b) ? (a) : (b))
typedef long long ll;
typedef unsigned long long ull;
using namespace std;
template <class T>
inline void read(T &x){
int f = 0, ch = 0; x = 0;
for(; !isdigit(ch); ch = getchar()) if(ch == '-') f = 1;
for(; isdigit(ch); ch = getchar()) x = x * 10 + ch - '0';
if(f) x = -x;
}
int a[20][20], b[20][20], n, m, sa, sb, tot;
struct Point{
int a[20];
inline Point(){ memset(a, 0, sizeof(a)); }
inline ull get(){
ull ans = 0;
for(int i = 1; i <= n; i++) ans = ans * 233 + a[i];
return ans;
}
};
map<ull, int> f[2], vis[2];
inline int dfs(Point now, int s){
ull ss = now.get();
if(vis[s][ss]) return f[s][ss];
int flag = 0; vis[s][ss] = 1, f[s][ss] = s ? 0 : inf;
for(int i = 1; i <= n; i++)
if((now.a[i] < now.a[i-1] || i == 1) && now.a[i] < m){
flag = 1; Point nxt = now; nxt.a[i]++;
int val = a[i][now.a[i]+1] + b[i][now.a[i]+1];
if(!s)
f[s][ss] = min(f[s][ss], dfs(nxt, s ^ 1));
else
f[s][ss] = max(f[s][ss], val + dfs(nxt, s ^ 1));
}
return !flag ? 0 : f[s][ss];
}
int main(){
read(n), read(m);
for(int i = 1; i <= n; i++)
for(int j = 1; j <= m; j++) read(a[i][j]);
for(int i = 1; i <= n; i++)
for(int j = 1; j <= m; j++) read(b[i][j]), sb += b[i][j];
Point fi; cout << dfs(fi, 1) - sb << endl;
return 0;
}