ROOM 密室 状压DP 记忆化搜索

模拟赛 T3   room

可以看出,n很小,不超过14,所以有很多做法。

可以状压DP、记忆化搜索。

下面是有点奇怪的做法……,不是很好理解。(有点像状压……吧)(没来得及加注释)

#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
using namespace std;
int n,ro[16],go[16],rk[16],gk[16],wk[16],key[16],f[20010][210],tot,ans;
int main()
{
	freopen("room.in","r",stdin);
	freopen("room.out","w",stdout);
	scanf("%d",&n);
	for(int i=0;i<n;i++)	scanf("%d",&ro[i]);
	for(int i=0;i<n;i++)	scanf("%d",&go[i]);
	for(int i=0;i<n;i++)	scanf("%d",&rk[i]);
	for(int i=0;i<n;i++)	scanf("%d",&gk[i]);
	for(int i=0;i<n;i++)	scanf("%d",&wk[i]);
	scanf("%d%d%d",&key[0],&key[1],&key[2]);
	tot=key[0]+key[1]+key[2];
	memset(f,-1,sizeof(f));
	f[0][key[0]]=key[2];
	for(int i=0;i<(1<<n);i++)
	{
		int k0=key[0],k1=key[1],r=tot;
		for(int j=0;j<n;j++)
			if((i>>j)&1)
			{
				k0+=rk[j];
				r+=rk[j]+gk[j]+wk[j]-ro[j]-go[j];
			}
		for(int j=0;j<=k0;j++)
		{
			if(f[i][j]==-1)	continue;
			int t=r-f[i][j]-j;
			for(int k=0;k<n;k++)
			{
				if((i>>k)&1)	continue;
				int p=max(0,ro[k]-j),q=max(0,go[k]-t);
				int &h=f[i|(1<<k)][max(0,j-ro[k])+rk[k]];
				if(f[i][j]>=p+q)
					h=max(h,f[i][j]-p-q+wk[k]);
			}
			ans=max(ans,j+t+f[i][j]);
		}
	}
	printf("%d\n",ans);
	return 0;
}

还有记忆化搜索的。

记忆化搜索就很棒了,很好理解。(注释比较详细就不多说了)(还是要状态压缩

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
int n,ans,ro[16],go[16],rk[16],gk[16],wk[16],r,g,w;
bool v1[100005],v2[16];//v1存是否搜过该状态,v2存是否开过该密室 
void dfs(int no,int r,int g,int w)//no用二进制表示状态 
{
	if(v1[no])	return;//剪枝,搜过该状态就不用再搜了 
	ans=max(ans,r+g+w);//更新答案 
	v1[no]=1;//将该状态记为搜过 
	for(int i=1;i<=n;i++)
		if(!v2[i])
		{
			int r_=r-ro[i],g_=g-go[i],w_=w+min(r_,0)+min(g_,0);
			if(w_<0)	continue;//用白色钥匙无法补齐红、绿钥匙与锁的差距 
			r_=max(r_,0)+rk[i];
			g_=max(g_,0)+gk[i];
			w_+=wk[i];
			v2[i]=1;//将该密室记为开过 
			dfs(no|(1<<(i-1)),r_,g_,w_);//当前状态下继续搜 
			v2[i]=0;//回溯 
		}
}
int main()
{
	freopen("room.in","r",stdin);
	freopen("room.out","w",stdout);
	scanf("%d",&n);
	for(int i=1;i<=n;i++)	scanf("%d",&ro[i]);
	for(int i=1;i<=n;i++)	scanf("%d",&go[i]);
	for(int i=1;i<=n;i++)	scanf("%d",&rk[i]);
	for(int i=1;i<=n;i++)	scanf("%d",&gk[i]);
	for(int i=1;i<=n;i++)	scanf("%d",&wk[i]);
	scanf("%d%d%d",&r,&g,&w);
	dfs(0,r,g,w);
	printf("%d\n",ans);
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值