hdu4568 Hunter(bfs+优先队列+旅行商问题)

Hunter

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 524    Accepted Submission(s): 149


Problem Description
  One day, a hunter named James went to a mysterious area to find the treasures. James wanted to research the area and brought all treasures that he could.
  The area can be represented as a N*M rectangle. Any points of the rectangle is a number means the cost of research it,-1 means James can't cross it, James can start at any place out of the rectangle, and explore point next by next. He will move in the rectangle and bring out all treasures he can take. Of course, he will end at any border to go out of rectangle(James will research every point at anytime he cross because he can't remember whether the point are researched or not).
  Now give you a map of the area, you must calculate the least cost that James bring out all treasures he can take(one point up to only one treasure).Also, if nothing James can get, please output 0.
 

Input
  The input consists of T test cases. The number of test cases T is given in the first line of the input. Each test case begins with a line containing 2 integers N M , (1<=N,M<=200), that represents the rectangle. Each of the following N lines contains M numbers(0~9),represent the cost of each point. Next is K(1<=K<=13),and next K lines, each line contains 2 integers x y means the position of the treasures, x means row and start from 0, y means column start from 0 too.
 

Output
  For each test case, you should output only a number means the minimum cost.
 

Sample Input
  
  
2 3 3 3 2 3 5 4 3 1 4 2 1 1 1 3 3 3 2 3 5 4 3 1 4 2 2 1 1 2 2
 

Sample Output
  
  
8 11
 

Source
 
思路:求出任意两宝藏间的距离(从i到i+1时,不含i的价值)和每个点离边界最短距离(不包含本身的价值),再用状态压缩求出最小花费
这个题我真是做了一天的,最开始写的是一般的bfs,三维数组标记,结果不对,测试时发现在求两点之间最短距离时出了问题,可能是非最短路径把最短路径给标记了,导致程序找不到正确的最短路径,因为这个题它是带有权值的,不是一般的bfs,于是全部删掉重写,用优先队列写。
求出每个宝藏到其他宝藏的最短距离(注意这里的最短路径不包括它本身的价值,比如a->b的最短路径,并不包括a这个点的价值,这是为了避免多加某一点的值)
现在再看看这道题,感觉真的不难,一个优先队列+bfs求出任意两点间的最短距离,接着一个状态压缩求出总共的最小花费

#include<stdio.h>
#include<iostream>
#include<queue>
#define inf 10000000
using namespace std;

int map[220][220],vis[220][220],dp[1<<14][15],dis1[15],dis2[15][15],istre[220][220],n,m,k;
int dx[]={1,-1,0,0},dy[]={0,0,-1,1};
struct node{
	int x,y,cost;
	friend bool operator<(node a,node b)
	{
		return a.cost>b.cost;
	}
}tre[15];

int min(int x,int y)
{
	return x<y?x:y;
}

int isbound(int x,int y)
{
	if(x==0||x==n-1||y==0||y==m-1)
		return 1;
	return 0;
}

int iscan(int x,int y)
{
	if(x>=0&&x<n&&y>=0&&y<m)
		return 1;
	return 0;
}

void dij(int ii)
{
	memset(vis,0,sizeof(vis));
	//memset();
	priority_queue<node>q;
	node a,temp;
	int x,y,i,count=0;
	a=tre[ii];
	a.cost=0;
	q.push(a);
	vis[a.x][a.y]=1;
	while(!q.empty())
	{
		a=q.top();
		q.pop();
		if(isbound(a.x,a.y))
			dis1[ii]=min(dis1[ii],a.cost);//到边界的最短距离
		if(istre[a.x][a.y]!=-1)
		{
			dis2[ii][istre[a.x][a.y]]=a.cost;//每个点只会找到其他点一次
			count++;
		}
		if(a.cost>dis1[ii]&&count==k)//这是用来剪枝的,后面的所有花费已经比a.cost大了并且到其他点的距离也都已找到就退出
			return ;
		for(i=0;i<4;i++)
		{
			x=a.x+dx[i];
			y=a.y+dy[i];
			if(iscan(x,y))
			{
				if(!vis[x][y]&&map[x][y]!=-1)
				{
					vis[x][y]=1;
					temp.x=x;
					temp.y=y;
					temp.cost=a.cost+map[x][y];
					q.push(temp);
				}
			}
		}

	}
}
int main()
{
	int i,j,t,p,mins;
	scanf("%d",&t);
	while(t--)
	{
		scanf("%d%d",&n,&m);
		for(i=0;i<n;i++)
			for(j=0;j<m;j++)
				scanf("%d",&map[i][j]);
		scanf("%d",&k);
		memset(istre,-1,sizeof(istre));
		for(i=0;i<k;i++)
		{
			scanf("%d%d",&tre[i].x,&tre[i].y);
			istre[tre[i].x][tre[i].y]=i;
		}
		for(i=0;i<(1<<k);i++)
			for(j=0;j<=k;j++)
				dp[i][j]=inf;
		for(i=0;i<k;i++)
		{
			dis1[i]=inf;
			dis2[i][i]=0;
			dij(i);
			//printf("%d %d\n",i,dis1[i]);
		}
		for(i=0;i<k;i++)
		{
			dp[1<<i][i]=dis1[i]+map[tre[i].x][tre[i].y];//初始化dp,从i进去的加上本身的花费和到边界的最小花费
		}
		for(i=0;i<(1<<k);i++)
		{
			for(j=0;j<k;j++)
			{
				if(dp[i][j]==inf)
					continue;
				if(!(i&(1<<j)))
					continue;
				for(p=0;p<k;p++)
				{
					if(i&(1<<p))
						continue;
					dp[i|(1<<p)][p]=min(dp[i|(1<<p)][p],dp[i][j]+dis2[j][p]);
				}
			}
		}
		mins=inf;
		for(i=0;i<k;i++)
		{
			mins=min(mins,dp[(1<<k)-1][i]+dis1[i]);//出来的时候再加上出来那个宝藏到边界的最小花费
		}
		if(mins!=inf)
			printf("%d\n",mins);
		else printf("0\n");
	}
	return 0;
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值