20200515 练习:轮廓线dp

T1 P4363 [九省联考2018]一双木棋chess

P4363 [九省联考2018]一双木棋chess
题目描述
菲菲和牛牛在一块 nm 列的棋盘上下棋,菲菲执黑棋先手,牛牛执白棋后手。 棋局开始时,棋盘上没有任何棋子,两人轮流在格子上落子,直到填满棋盘时结束。

落子的规则是:一个格子可以落子当且仅当这个格子内没有棋子且这个格子的左侧及上方的所有格子内都有棋子。

棋盘的每个格子上,都写有两个非负整数,从上到下第 i 行中从左到右第 j 列的格 子上的两个整数记作 A i , j 、 B i , j A_{i,j}、B_{i,j} Ai,jBi,j。在游戏结束后,菲菲和牛牛会分别计算自己的得分:菲菲的得分是所有有黑棋的格子上的 A i , j A_{i,j} Ai,j 之和,牛牛的得分是所有有白棋的格子上的 B i , j B_{i,j} Bi,j 的和。

菲菲和牛牛都希望,自己的得分减去对方的得分得到的结果最大。现在他们想知道,在给定的棋盘上,如果双方都采用最优策略且知道对方会采用最优策略,那么,最终的结果如何。

输入格式
输入第一行包含两个正整数 nm,保证 n , m ≤ 10 n,m \leq10 n,m10

接下来 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,m10Ai,j,Bi,j100000

思路:
考虑 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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值