hdu 3681

题意:一个机器人想越狱,他只能带一定电量的电池,'S'表示道路可行,'G'表示充电器,只可充电一次,但是可以经过很多次。'F'表示起点,'Y'表示要破坏的机关,也是只能破坏一次,但是可以经过无数次。'D'表示不能经过的地点。求他能破坏所有机关,带的最小初始电量。

虽然知道要用dp+状态压缩,但是我还是去想了想搜索,因为可以走无数次,显然bfs不可行,dfs也不行。后面就只能去看题解,原来是一题TSP的变式题,题中G和Y都只能有一次效果,但可以经过无数次,除了D不能走以外,其他的都可以走无数次,但是肯定要到Y,G可能要到达。由于只有一次效果,我们只要他经过所有Y即可,只能经过1次,TSP很明显,我们只要对关键点保存状态就可以了之后只要二分枚举答案,再判断一下就好了,判断的时候dp一下就ok了,注意最短路的处理,bfs显然。wa一次就a了,看来放松后回来状态还不错。

Run IDSubmit TimeJudge StatusPro.IDExe.TimeExe.MemoryCode Len.LanguageAuthor
44942542011-08-25 23:46:43Accepted3681562MS4356K2363 BC++xym2010
#include<cstdio>
#include<cstring>
#include<string>
#include<iostream>
#include<cmath>
#include<algorithm>
#include<queue>
using namespace std;
char gp[16][16];
int nd[16][2],dp[1<<16][16],n,m,fis,ac,cnt=0,dis[16][16];
int dr[4][2]={1,0,-1,0,0,1,0,-1};
int bfs(int x,int y,int xx,int yy)
{
	queue<int> a,b;
	int vd[16][16];
	memset(vd,-1,sizeof(vd));
	if(xx==x&&y==yy) return 0;
	a.push(x);
	b.push(y);
	vd[x][y]=0;
	while(a.empty()==0)
	{
		int nx=a.front(),ny=b.front();
		a.pop();b.pop();
		for(int i=0;i<4;i++)
		{
			int dx=nx+dr[i][0],dy=ny+dr[i][1];
			if(dx<0||dx>=n||dy<0||dy>=m||vd[dx][dy]!=-1||gp[dx][dy]=='D')continue;
			vd[dx][dy]=vd[nx][ny]+1;
			a.push(dx);b.push(dy);
			if(dx==xx&&dy==yy)return vd[dx][dy];
		}
	}
	return -1;
}
bool check(int gs)
{
	memset(dp,-1,sizeof(dp));
	dp[1<<fis][fis]=gs;
	int ans=-1;
	for(int i=0;i<(1<<cnt);i++)
		for(int j=0;j<cnt;j++)
		{
			if((i&(1<<j))==0)continue;
			if((i&(ac))==ac)
				if(dp[i][j]!=-1)
					return true;
			for(int k=0;k<cnt;k++)
			{
				if(dp[i][j]==-1||k==j||(i&(1<<k))||dis[j][k]==-1)continue;
				int tem=dp[i][j]-dis[j][k];
				if(tem<0)continue;
				dp[i+(1<<k)][k]=max(dp[i+(1<<k)][k],tem);
				if(gp[nd[k][0]][nd[k][1]]=='G')dp[i+(1<<k)][k]=gs;
			}
		}
		return false;
}
void init()
{
	int i,j;
	for(int i=0;i<cnt;i++)
		for(int j=0;j<cnt;j++)
		{
			if(i==j)dis[i][j]=0;
			else
				dis[i][j]=bfs(nd[i][0],nd[i][1],nd[j][0],nd[j][1]);
		}
}
int main()
{  
	int l,r,mid;
	while(1)
	{
		scanf("%d%d",&n,&m);
		if(n==0&&m==0)return 0;
		getchar();
	    ac=0;cnt=0;
		for(int i=0;i<n;i++)
		{
			for(int j=0;j<m;j++)
			{
				scanf("%c",&gp[i][j]);
				if(gp[i][j]=='F')
				{
					fis=cnt;
					nd[cnt][0]=i;
					nd[cnt][1]=j;
					ac+=1<<cnt;
					cnt++;
				}
				else
					if(gp[i][j]=='G')
					{
						nd[cnt][0]=i;
						nd[cnt][1]=j;
						cnt++;
					}
					else
						if(gp[i][j]=='Y')
						{
							nd[cnt][0]=i;
							nd[cnt][1]=j;
							ac+=1<<cnt;
							cnt++;
						}
			}
			getchar();
		}
		init();
		l=0,r=300;
		int ans=1<<30;
		while(l<=r)
		{
			mid=(l+r)/2;
			if(check(mid))
			{
				r=mid-1;
				ans=min(mid,ans);
			}
			else
				l=mid+1;
		}
		if(ans==(1<<30))
			printf("-1\n");
		else
			printf("%d\n",ans);
	}
	return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值