BZOJ 5248: [2018多省省队联测]一双木棋

题目大意:

一个网格,两个人轮流放棋子,一个格子能放当且仅当这个格子的左面和上面已经填满。两个人每放一个格子都会获得相应的得分。

目标:自己得分-对方得分尽可能大。

问最终得分差。

题解:
状压DP,用0/1来表示轮廓线。

代码:

#include<cstdio>
#include<algorithm>
using namespace std;
int n,m,f[1<<20][2],a[15][15],b[15][15];
int dfs(int s,int cas){
	if (cas==0 && f[s][cas]!=-1e9) return f[s][cas];
	else if (cas==1 && f[s][cas]!=1e9) return f[s][cas];
	int ans;
	if (cas==0) ans=-1e9;
	else ans=1e9;
	int x=n+1,y=1;
	if (s&(1<<(n+m-1))) x--;
	else y++;
	for (int i=2; i<=n+m; i++){
		int pre=s&(1<<(n+m-i+1));
		int now=s&(1<<(n+m-i));
		if (pre && !now){
			int to=s-(1<<(n+m-i+1))+(1<<(n+m-i));
			if (cas==0) ans=max(ans,a[x][y]+dfs(to,1));
			else ans=min(ans,dfs(to,0)-b[x][y]);
		}
		if (now) x--;
		else y++;
	}
	f[s][cas]=ans;
	return ans;
}
int main(){
	scanf("%d%d",&n,&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]);
	int s=0;
	for (int i=1; i<=n; i++){
		s=s<<1;
		s|=1;
	}
	for (int i=1; i<=m; i++) s=s<<1;
	for (int i=0; i<(1<<n+m); i++) f[i][0]=-1e9,f[i][1]=1e9;
	int t=0;
	for (int i=1; i<=n; i++){
		t=t<<1;
		t|=1;
	}
	f[t][0]=f[t][1]=0;
	printf("%d\n",dfs(s,0));
	return 0;
}

  

posted @ 2018-04-10 14:14  ~Silent  阅读(124)  评论(0编辑  收藏  举报
Live2D