[BZOJ1187][HNOI2007] 神奇游乐园 - 插头DP

这么裸的题调得我一脸懵逼 - - 我太弱啦

#include"bits/stdc++.h"
#define F(i,l,r) for(register int i=l;i<=r;i++)
#define readi(x) scanf("%d",&x)
using namespace std;
const int N=105,M=7,S=3005,inf=0x3f3f3f3e;
int dp[N][M][S],n,m,w[N][M],lim,st[S],rk[2<<2*M+2],cnt[2<<2*M],ans=-inf;
void build_status(){
	F(i,0,(1<<2*m+2)-1){
		int p=i,s=0;
		bool flag=true;
		while (p){
			if(p%4==3){flag=false;break;}
			if(p%4==1)s++,cnt[i]++;if(p%4==2)s--;
			p>>=2;if(s<0){flag=false;break;}
		}
		if(s)flag=false;
		if(flag)st[++lim]=i,rk[i]=lim;
	} 
}
int main(){
	readi(n),readi(m);build_status();
	F(i,1,n)F(j,1,m)readi(w[i][j]);
	F(i,1,n)F(j,0,m)F(k,1,lim)dp[i][j][k]=-inf;
	dp[1][0][1]=0;
	F(i,1,n){
		F(j,0,m-1)F(k,1,lim)if(dp[i][j][k]>-inf){
			int s1=(st[k]>>2*j)&3; //左插头
			int s2=(st[k]>>2*j+2)&3; //上插头
			if (s1==1 && s2==2){ //插头组成回路 : 若只有一条回路记录答案 多条回路直接continue 
				if (cnt[st[k]]==1) ans=max(ans,dp[i][j][k]+w[i][j+1]);
				else continue;
			} else if (s1==2 && s2==1){ //两区间合并 
				int p=rk[st[k]-(s1<<2*j)-(s2<<2*j+2)];
				dp[i][j+1][p]=max(dp[i][j+1][p],dp[i][j][k]+w[i][j+1]);
			} else if (!s1 && !s2){ //空的 
				int p1=k,p2=rk[st[k]+(1<<2*j)+(2<<2*j+2)];
				dp[i][j+1][p1]=max(dp[i][j+1][p1],dp[i][j][k]);
				dp[i][j+1][p2]=max(dp[i][j+1][p2],dp[i][j][k]+w[i][j+1]);
			} else if (!s1 || !s2){ //一端为空 
				int p1=k,p2=s1?rk[st[k]+(3*s1<<2*j)]:rk[st[k]-(3*s2<<2*j)];
				dp[i][j+1][p1]=max(dp[i][j+1][p1],dp[i][j][k]+w[i][j+1]);
				dp[i][j+1][p2]=max(dp[i][j+1][p2],dp[i][j][k]+w[i][j+1]);
			} else { //均为1或均为2 
				if (s1&1){ //上插头的对应插头变成1  
					int p=j+2;while(((st[k]>>2*p)&3)!=2)p++;
					p=rk[st[k]-(1<<2*p)-(s1<<2*j)-(s2<<2*j+2)];
					dp[i][j+1][p]=max(dp[i][j+1][p],dp[i][j][k]+w[i][j+1]);
				} else { //左插头的对应插头变成2 
					int p=j-1;while(((st[k]>>2*p)&3)!=1)p--;
					p=rk[st[k]+(1<<2*p)-(s1<<2*j)-(s2<<2*j+2)];
					dp[i][j+1][p]=max(dp[i][j+1][p],dp[i][j][k]+w[i][j+1]);
				}
			}
		}
		if(i!=n)F(k,1,lim)if(rk[st[k]<<2])
			dp[i+1][0][rk[st[k]<<2]]=max(dp[i+1][1][rk[st[k]<<2]],dp[i][m][k]);
	}
	cout << ans << endl;
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值