2019.02.19【HNOI2007】【BZOJ1187】【洛谷P3190】神奇游乐园(轮廓线DP)

BZOJ传送门

洛谷传送门


解析:

还是轮廓线DP裸题,不过有两个细节需要注意一下。

我的写法是从上向下同时从左向右,括号序列转移。每个插头的转移是从左边和上面,以下的叙述就是按照这个转移方式来的。

由于不要求覆盖所有点,只是回路,所以当上方和左方都没有插头的时候,我们可以不强制经过这个点。需要将原状态向下继承。

更新答案的时候,由于是要求只能有一个回路,所以只会在当我们上方和左方的插头是一对匹配的括号,且当前只有这一对括号的时候找到一条新的回路,这时候才能更新答案。


代码:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define re register
#define cs const

cs int INF=0x3f3f3f3f;
struct Map{
	static cs int magic=189859;
	int key[magic],val[magic];
	int sta[magic],top;
	
	Map(){memset(key,-1,sizeof key);memset(val,-INF,sizeof val);}
	
	struct iterator{
		Map *belong;
		int now;
		iterator(Map *a,cs int &_now):belong(a),now(_now){}
		int key(){return belong->key[belong->sta[now]];}
		int val(){return belong->val[belong->sta[now]];}
		iterator operator++(){
			++now;
			return *this;
		}
		bool operator!=(cs iterator &b){
			return now!=b.now||belong!=b.belong;
		}
	};
	
	iterator begin(){return iterator(this,1);}
	iterator end(){return iterator(this,top+1);}
	
	cs int &operator[](cs int &k)cs{
		int h=k%magic;
		while((~key[h])&&(key[h]^k))h=(h+1)%magic;
		return val[h];
	}
	
	int &operator[](cs int &k){
		int h=k%magic;
		while((~key[h])&&(key[h]^k))h=(h+1)%magic;
		if(key[h]^k){
			sta[++top]=h;
			key[h]=k;
		}
		return val[h];
	}
	
	void clear(){
		while(top){
			key[sta[top]]=-1;
			val[sta[top]]=-INF;
			--top;
		}
	}
	
}ma[2];

inline void ckmax(int &a,cs int &b){a<b?a=b:0;}

int n,m,pre;
int val[150][150]; 
int bits[100];

inline int get_state(int s,int j){
	return (s&bits[j])>>(j<<1);
}

inline int set_state(int s,int j,int b){
	return (s&~bits[j])|(b<<(j<<1));
}

inline int set_state(int s,int j,int bn,int bj){
	return (s&~(bits[j]|bits[j+1]))|((bn|(bj<<2))<<(j<<1));
}

inline int find_match(int s,int j){
	int c=get_state(s,j),d=c==1?1:-1,f=0;
	for(;;j+=d){
		if(get_state(s,j)==c)++f;
		else if(get_state(s,j))--f;
		if(f==0)return j;
	}
	cout<<"-1";exit(0);
}

int ans=-INF;
inline void dp(){
	ma[0].clear(),ma[1].clear();pre=0;
	ma[pre][0]=0;
	for(int re i=0;i<n;++i)
	for(int re j=0;j<m;++j){
		int now=pre^1;
		ma[now].clear();
		for(Map::iterator it=ma[pre].begin();it!=ma[pre].end();++it){
			int s=it.key(),nowans=it.val();
			if(j==0)s<<=2;
			int sl=get_state(s,j),su=get_state(s,j+1);
			if(!sl&&!su){
				ckmax(ma[now][s],nowans);
				if(i+1<n&&j+1<m)ckmax(ma[now][set_state(s,j,1,2)],nowans+val[i][j]);
			}
			else if(!sl||!su){
				if(i+1<n)ckmax(ma[now][set_state(s,j,su|sl,0)],nowans+val[i][j]);
				if(j+1<m)ckmax(ma[now][set_state(s,j,0,su|sl)],nowans+val[i][j]);
			}
			else if(sl==su){
				int posl=find_match(s,j),posu=find_match(s,j+1);
				int mn=min(posl,posu),mx=max(posl,posu);
				int bs=set_state(s,mn,1);
				bs=set_state(bs,mx,2);
				ckmax(ma[now][set_state(bs,j,0,0)],nowans+val[i][j]);
			}
			else if(sl==2&&su==1){
				ckmax(ma[now][set_state(s,j,0,0)],nowans+val[i][j]);
			}
			else if(sl==1&&su==2){
				if(s==set_state(0,j,sl,su))ckmax(ans,nowans+val[i][j]);
			}
		}
		pre=now; 
	}
}

signed main(){
	scanf("%d%d",&n,&m);
	for(int re j=0;j<=m;++j)bits[j]=3<<(j<<1);
	for(int re i=0;i<n;++i)
	for(int re j=0;j<m;++j){
		scanf("%d",&val[i][j]);
	}
	dp();
	cout<<ans<<"\n";
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值