T1 P4363 [九省联考2018]一双木棋chess
P4363 [九省联考2018]一双木棋chess
题目描述
菲菲和牛牛在一块 n 行 m 列的棋盘上下棋,菲菲执黑棋先手,牛牛执白棋后手。 棋局开始时,棋盘上没有任何棋子,两人轮流在格子上落子,直到填满棋盘时结束。
落子的规则是:一个格子可以落子当且仅当这个格子内没有棋子且这个格子的左侧及上方的所有格子内都有棋子。
棋盘的每个格子上,都写有两个非负整数,从上到下第 i 行中从左到右第 j 列的格 子上的两个整数记作 A i , j 、 B i , j A_{i,j}、B_{i,j} Ai,j、Bi,j。在游戏结束后,菲菲和牛牛会分别计算自己的得分:菲菲的得分是所有有黑棋的格子上的 A i , j A_{i,j} Ai,j 之和,牛牛的得分是所有有白棋的格子上的 B i , j B_{i,j} Bi,j 的和。
菲菲和牛牛都希望,自己的得分减去对方的得分得到的结果最大。现在他们想知道,在给定的棋盘上,如果双方都采用最优策略且知道对方会采用最优策略,那么,最终的结果如何。
输入格式
输入第一行包含两个正整数 n,m,保证
n
,
m
≤
10
n,m \leq10
n,m≤10。
接下来 n 行,每行 m 个非负整数,按从上到下从左到右的顺序描述每个格子上的 第一个非负整数:其中第 i 行中第 j 个数表示 A i , j A_{i,j} Ai,j。
接下来 n 行,每行 m 个非负整数,按从上到下从左到右的顺序描述每个格子上的 第二个非负整数:其中第 i 行中第 j 个数表示 B i , j B_{i,j} Bi,j。
输出格式
输出一个整数,表示菲菲的得分减去牛牛的得分的结果。
输入输出样例
输入
2 3
2 7 3
9 1 2
3 7 2
2 3 1
输出
2
对于所有的测试数据, n , m ≤ 10 , A i , j , B i , j ≤ 100000 n,m\leq 10 ,A_{i,j},B_{i,j}\leq 100000 n,m≤10,Ai,j,Bi,j≤100000。
思路:
考虑 dp:
棋盘上被占据的格子相同的情况为同一状态
对于一个状态 S,转移到下一个状态 T,维护最优的 ans
- 如果第一个人下,有 d p [ S ] = m a x ( d p [ S ] , d p [ T ] + a [ x ] [ y ] ) dp[S]=max(dp[S],dp[T]+a[x][y]) dp[S]=max(dp[S],dp[T]+a[x][y])
- 如果第二个人下,有 d p [ S ] = m a x ( d p [ S ] , d p [ T ] − b [ x ] [ y ] ) dp[S]=max(dp[S],dp[T]-b[x][y]) dp[S]=max(dp[S],dp[T]−b[x][y])
考虑压缩状态:
对于每个状态,二进制压缩,从右上到左下,分界线向下为 0,向左为 1,每个状态压为最多 20 位。
如上状态为 01100101
如何转移状态?
找到如 10 的位置,即可下棋的位置
下一个状态即把 10,改成 01
有
2
20
2^{20}
220 种不同状态,直接 dp 即可
代码:
#include<bits/stdc++.h>
using namespace std;
#define in Read()
#define re register
inline int in{
int s=0,f=1;char x;
for(x=getchar();x<'0'||x>'9';x=getchar()) if(x=='-') f=-1;
for( ;x>='0'&&x<='9';x=getchar()) s=(s*10)+(x&15);
return f==1?s:-s;
}
const int A=20;
const int N=3e6+5;
const int INF=1e9;
int n,m;
int val[2][A][A];
int dp[N];
inline int DP(int now,int who){
if(dp[now]!=-INF) return dp[now];
dp[now]=who?-INF:INF;
int x=n,y=0;
for(re int i=0;i<n+m-1;++i){
if((now>>i)&1) x--;
else y++;
if(((now>>i)&3)!=1) continue;
int nex=now^(3<<i);
if(who) dp[now]=max(dp[now],DP(nex,who^1)+val[0][x][y]);
else dp[now]=min(dp[now],DP(nex,who^1)-val[1][x][y]);
}
return dp[now];
}
signed main(){
n=in,m=in;
for(re int i=0;i<n;++i)
for(re int j=0;j<m;++j)
val[0][i][j]=in;
for(re int i=0;i<n;++i)
for(re int j=0;j<m;++j)
val[1][i][j]=in;
fill(dp,dp+(1<<20)+1,-INF);
int end=(((1<<n)-1)<<m);
dp[end]=0;
printf("%d\n",DP((1<<n)-1,1));
return 0;
}