bzoj2310 ParkII 插头dp

3 篇文章 0 订阅

Description


Hnoi2007-Day1有一道题目 Park:给你一个 m * n 的矩阵,每个矩阵内有个
权值V(i,j) (可能为负数),要求找一条回路,使得每个点最多经过一次,并且经过
的点权值之和最大,想必大家印象深刻吧.
无聊的小 C 同学把这个问题稍微改了一下:要求找一条路径,使得每个点
最多经过一次,并且点权值之和最大,如果你跟小 C 一样无聊,就麻烦做一下
这个题目吧.

30%的数据,n≤6.
100%的数据,m<=100,n ≤ ≤8.
注意:路径上有可能只有一个点.

Solution


讲个鬼故事,插头dp

对于非闭合回路的问题我们可以添加一类新状态——单身插头(雾,单身插头表示的是一条路径在轮廓线上的一个端点而非一对括号中的一个,因此没有括号与之配对并且不参与括号配对。如果出现了2个单身插头或者有且仅有1个单身插头更新答案就行了。并且由单身插头的定义可知,轮廓线上不可能出现超过2个单身插头。

含有单身插头的转移包括:
上/左无插头,可以新建向右/向下单身插头
上/左中一侧有非单身插头,可以把这个插头变成路径的一端,原本的另一端变成单身插头
上/左中一侧有单身插头,可以把插头延续或转弯
上/左中一侧有单身插头,另一侧有非单身插头,那么连起来并把非单身插头的另一端变成单身插头

可能说得不清楚,具体看代码

Code


#include <stdio.h>
#include <string.h>
#include <algorithm>
#define rep(i,st,ed) for (int i=st;i<=ed;++i)
#define fill(x,t) memset(x,t,sizeof(x))

int f[102][10][8400],bin[10],rc[105][9];
int rec[8400],rnk[262144],tot,n,m;
int mx;

int get(int S,int x) {
	return (S/bin[x])%4;
}

void update(int &x,int v) {
	(x<v)?(x=v):0;
}

void dfs(int dep,int S,int sum,int cnt) {
	if (cnt>2||sum<0||sum+dep-1>m) return ;
	if (dep>m) {
		rec[++tot]=S;
		rnk[S]=tot;
		return ;
	}
	dfs(dep+1,S,sum,cnt);
	dfs(dep+1,S+bin[dep],sum+1,cnt);
	dfs(dep+1,S+2*bin[dep],sum-1,cnt);
	dfs(dep+1,S+3*bin[dep],sum,cnt+1);
}

int rig(int S,int x) {
	for (int i=x,cnt=0;i<=m;++i) {
		int tmp=get(S,i);
		if (tmp==1) cnt++;
		else if (tmp==2) cnt--;
		if (!cnt) return bin[i];
	}
	return -1;
}

int lef(int S,int x) {
	for (int i=x,cnt=0;~i;--i) {
		int tmp=get(S,i);
		if (tmp==1) cnt++;
		else if (tmp==2) cnt--;
		if (!cnt) return bin[i];
	}
	return -1;
}

int main(void) {
	// freopen("data.in","r",stdin);
	bin[0]=1; rep(i,1,9) bin[i]=bin[i-1]*4;
	int ans=0; scanf("%d%d",&n,&m);
	dfs(0,0,0,0);
	rep(i,1,n) rep(j,1,m) scanf("%d",&rc[i][j]);
	// if (n<m) std:: swap(n,m);
	fill(f,-31); f[1][0][rnk[0]]=0;
	rep(i,1,n) {
		rep(j,1,m) {
			update(ans,rc[i][j]);
			rep(k,1,tot) {
				int S=rec[k],p=get(S,j-1),q=get(S,j);
				int tmp=f[i][j-1][k]+rc[i][j];
				if (!p&&!q) {
					update(f[i][j][rnk[S+bin[j-1]+2*bin[j]]],tmp);
					update(f[i][j][rnk[S+3*bin[j-1]]],tmp);
					update(f[i][j][rnk[S+3*bin[j]]],tmp);
					update(f[i][j][k],f[i][j-1][k]);
				}
				if (!p&&q==1) {
					update(f[i][j][k],tmp);
					update(f[i][j][rnk[S-bin[j]+bin[j-1]]],tmp);
					update(f[i][j][rnk[S-bin[j]+rig(S,j)]],tmp);
				}
				if (!p&&q==2) {
					update(f[i][j][k],tmp);
					update(f[i][j][rnk[S-2*bin[j]+2*bin[j-1]]],tmp);
					update(f[i][j][rnk[S-2*bin[j]+2*lef(S,j)]],tmp);
				}
				if (!p&&q==3) {
					update(f[i][j][k],tmp);
					update(f[i][j][rnk[S-3*bin[j]+3*bin[j-1]]],tmp);
					if (S==q*bin[j]) update(ans,tmp);
				}
				if (p==1&&!q) {
					update(f[i][j][k],tmp);
					update(f[i][j][rnk[S-bin[j-1]+bin[j]]],tmp);
					update(f[i][j][rnk[S-bin[j-1]+rig(S,j-1)]],tmp);
				}
				if (p==2&&!q) {
					update(f[i][j][k],tmp);
					update(f[i][j][rnk[S-2*bin[j-1]+2*bin[j]]],tmp);
					update(f[i][j][rnk[S-2*bin[j-1]+2*lef(S,j-1)]],tmp);
				}
				if (p==3&&!q) {
					update(f[i][j][k],tmp);
					update(f[i][j][rnk[S-3*bin[j-1]+3*bin[j]]],tmp);
					if (S==p*bin[j-1]) update(ans,tmp);
				}
				if (p==1&&q==1) update(f[i][j][rnk[S-bin[j]-bin[j-1]-rig(S,j)]],tmp);
				if (p==1&&q==2&&S==bin[j-1]+2*bin[j]) update(ans,tmp);
				if (p==1&&q==3) update(f[i][j][rnk[S-3*bin[j]-bin[j-1]+rig(S,j-1)]],tmp);
				if (p==2&&q==1) update(f[i][j][rnk[S-2*bin[j-1]-bin[j]]],tmp);
				if (p==2&&q==2) update(f[i][j][rnk[S-2*bin[j-1]-2*bin[j]+lef(S,j-1)]],tmp);
				if (p==2&&q==3) update(f[i][j][rnk[S-3*bin[j]-2*bin[j-1]+2*lef(S,j-1)]],tmp);
				if (p==3&&q==1) update(f[i][j][rnk[S-3*bin[j-1]-bin[j]+rig(S,j)]],tmp);
				if (p==3&&q==2) update(f[i][j][rnk[S-3*bin[j-1]-2*bin[j]+2*lef(S,j)]],tmp);
				if (p==3&&q==3&&S==q*bin[j]+p*bin[j-1]) update(ans,tmp);
			}
		}
		if (i!=n) rep(k,1,tot) if (rec[k]%4==0) update(f[i+1][0][k],f[i][m][rnk[rec[k]/4]]);
	}
	printf("%d\n", ans);
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值