状压搜索 Circling Round Treasures:CodeForces - 375C

1 篇文章 0 订阅
1 篇文章 0 订阅

题目:Circling Round Treasures:CodeForces - 375C

题意:

给你一个N*M的地图(N,M<=20),地图上’#'表示障碍物,'B’表示炸弹,数字表示宝藏(炸弹个数+宝藏个数<=8),每个宝藏有价值(-200<=v<=200),'S’表示出发点。每次移动可以从一个格子移动到相邻格子。寻找一条路径从’S’出发再回到’S’的路径,移动步数记为K,这个路径所包围的宝藏价值总和记为V,则这条路径的价值为V-K。题目即是求可行的最大的路径价值,并且不能包围炸弹。

思路:

这里一定要注意到宝藏和炸弹个数和小于等于8个。这样就可以用状压+BFS了。用F[X][Y][S]记录最优状态,S是 2 8 2^8 28的状压,表示8个宝藏(或炸弹)目前是否被包含在路径中。F[X][Y][S]表示位于(X,Y)位置时,8个宝藏(或炸弹)包含状态为S时的路径最大价值。
一个宝藏(或炸弹)是否被包含,只要向右做一个射线,如果碰到奇数个路径点那就是包含,否则偶数为不包含。所以经过一个点时,同行左侧的宝藏(或炸弹)包含情况会被反转,重点都说完了,后面自己思考吧。

代码:

#include<iostream>
#include<cstdio>
using namespace std;
const int nex[4]={1,-1,0,0},ney[4]={0,0,-1,1},q_size=410*(1<<8);
int n,m,sx,sy,ans,cnt,va[10],vx[10],vy[10],qx[q_size],qy[q_size],ql[q_size],qs[q_size],b[23][23][1<<8];
char mp[23][23];
inline int get_si(int x,int y){
	int ans=0;
	for (int i=1;i<=cnt;++i) if (vx[i]==x&&vy[i]>y) ans|=1<<i-1;
	return ans;
}
int main(){
	scanf("%d%d",&n,&m);
	for (int i=1;i<=n;++i) scanf("%s",mp[i]+1);
	for (int i=1;i<=n;++i) for (int j=1;j<=m;++j) if (mp[i][j]>='0'&&mp[i][j]<='9') ++cnt,vx[mp[i][j]-48]=i,vy[mp[i][j]-48]=j;
	for (int i=1;i<=cnt;++i) scanf("%d",&va[i]);
	for (int i=1;i<=n;++i) for (int j=1;j<=m;++j) if (mp[i][j]=='B') va[++cnt]=-1e8,vx[cnt]=i,vy[cnt]=j;else if (mp[i][j]=='S') mp[i][j]='.',sx=i,sy=j;
	int h=0,t=1;
	qx[1]=sx;qy[1]=sy;
	while (++h<=t){
		if (qx[h]==sx&&qy[h]==sy){
			int sum=0;
			for (int i=1;i<=cnt;++i) if ((1<<i-1)&qs[h]) sum+=va[i];
			ans=max(ans,sum-ql[h]);
		}
		for (int i=0;i<4;++i){
			int xx=qx[h]+nex[i],yy=qy[h]+ney[i],no_s=xx!=qx[h]?qs[h]^get_si(min(xx,qx[h]),yy):qs[h];
			if (mp[xx][yy]=='.'&&!b[xx][yy][no_s]) b[xx][yy][no_s]=1,qx[++t]=xx,qy[t]=yy,ql[t]=ql[h]+1,qs[t]=no_s;
		}
	}
	printf("%d",ans);		
	return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值