HDU 4568 Hunter TSP_DP

原题:

Hunter

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


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
 



题意+题解:

一道需要先建图的TSP+状压问题。

经测试没有宝藏本身就不能走,或者两个宝藏之间被-1隔开不能想通的情况也不存在。如果都考虑到的话就需要写的再复杂些,先判断最大能取的个数和是哪些点,然后剩下的不能一次取到的数量较少的宝藏就直接舍去,再建图。

先做一遍BFS,或者SPFA也行,在map里搜索,加一个起点(终点),求出每个宝藏和起点之间的最短路。

这里从点a到点b的边,要计算b点的权值,而不计算a的权值,如果从b到a,则反之。因为TSP保证每个宝藏都有一个入度一个出度,所以宝藏该点的权值只被计算一次,所以就设置成这种起点不计算,终点计算的方法,防止重复计算。


BFS搜的话用优先队列,所有的结点只会入队列一次,比较快。

SPFA的话用标记减少入队列的次数也可以减少时间复杂度。


建好图后得到任意两点的距离,原图就可以丢掉了。下面就是TSP的问题了

一共有14个点(带虚拟的起点),状压开2^14-1就够了

dp[i][j]  表示 在状态i的情况下(这里i是一个二进制数,其中第k位为1的表示第k个结点已被遍历,0表示未被遍历),以j号结点为结束时的最小花费。

dp[i][j] = min(dp[i][j] , dp[i-(1<<j)][s] + dis(s,j));  表示在i中去掉j结点时,以s结点结尾的dp值+s到j的距离 的最小值


最后输出 dp[(1<<(k+1))-1][k]   其中k为虚拟的起点和终点,一共有0---k-1号宝藏

之前写了一个SPFA建图,标号是1--k的代码,给写残了,也给贴出来吧

代码:



//大二寒假集训 第一章DP G_Hunter
#include<cstdio>
#include<cstring>
#include<vector>
#include<queue>
#include<cmath>
#include<iostream>
#include<algorithm>
#include<functional>
#define MAXN 99999999
using namespace std;
typedef struct MyStruct
{
	int x,y;
}Treasure;
typedef struct MyStruct1
{
	int x,y,cost;
	bool operator <(const MyStruct1 &a) const
	{
		return a.cost < cost;
	}
}POINT;

Treasure point[15];
int map[210][210];
int dis[15][15];
int n,m,k;
int dp[17000][15];
int cost[210][210];
int add[4][2]={{1,0},{0,1},{-1,0},{0,-1}};
bool vis[210][210];
bool flag[210][210];
void bfs_que(int num)
{
	memset(vis,0,sizeof(vis));
	int i,j,go_out=MAXN,flag_out=0,count=0;
	for ( i = 0; i <=n ; i++)
		for ( j = 0; j <=m ; j++)
			cost[i][j]=MAXN;
	POINT temp,newd;
	temp.x=point[num].x;
	temp.y=point[num].y;
	temp.cost=0;
	vis[temp.x][temp.y]=1;
	priority_queue<POINT>Q;
	Q.push(temp);
	while (!Q.empty())
	{
		if(count==k)break;
		temp=Q.top();
		Q.pop();
		if(temp.x==0 || temp.x==n-1 || temp.y==0 || temp.y==m-1)
		{
			if(!flag_out)
			{
				flag_out=1;
				count++;
				go_out=temp.cost;
			}
		}
		for ( i = 0; i <4 ; i++)
		{
			newd=temp;
			newd.x+=add[i][0];
			newd.y+=add[i][1];
			if(newd.x>=0 && newd.x<n && newd.y>=0 && newd.y<m && map[newd.x][newd.y]!=-1 && vis[newd.x][newd.y]==0)
			{
				newd.cost+=map[newd.x][newd.y];
				if(newd.cost<cost[newd.x][newd.y])
				{
					if(flag[newd.x][newd.y])count++;
					vis[newd.x][newd.y]=1;
					cost[newd.x][newd.y]=newd.cost;
					Q.push(newd);
				}
			}
		}
	}
	while (!Q.empty()){Q.pop();}
	for ( i = 0; i < k ; i++)
	{
		dis[num][i]=cost[point[i].x][point[i].y];
	}
	dis[num][k]=go_out;
	dis[k][num]=go_out+map[point[num].x][point[num].y];
}

int main()
{
	int t,i,j,s,temp,ans,states;
	scanf("%d",&t);
	while (t--)
	{
		memset(flag,0,sizeof(flag));
		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);
		for(i=0;i<k;i++)
		{
			scanf("%d%d",&point[i].x,&point[i].y);
			flag[point[i].x][point[i].y]=1;
		}
		
		if (k==0)
		{
			printf("0\n");
			continue;
		}
		states= (1<<(k+1))-1;

		for ( i = 0; i < k; i++)bfs_que(i);

		for ( i = 0; i <= states; i++)
			for ( j = 0; j <=k ; j++)
				dp[i][j]=MAXN;		
		for ( i = 0; i < k; i++)
		{
			dp[1<<i][i]=dis[k][i];
		}
		for ( i = 0; i <= states; i++)
		{
			for ( j = 0; j <= k ; j++)
			{
				for ( s = 0; s <= k ; s++)
				{
					if(s!=j && (i & (1<<j)) && (i & (1<<s)) && (temp= (i- (1<<j)) , dp[temp][s]!=MAXN) && dp[i][j]> dp[temp][s]+dis[s][j])
					{
						dp[i][j]=dp[temp][s]+dis[s][j];
					}
				}
			}
		}

		printf("%d\n",dp[states][k]);
	}

	return 0;
}







/* WA 版 
//大二寒假集训 第一章DP G_Hunter
#include<cstdio>
#include<cstring>
#include<vector>
#include<queue>
#include<cmath>
#include<iostream>
#include<algorithm>
#include<functional>
#define MAXN 9999999
using namespace std;
typedef struct MyStruct
{
	int x,y;
}Treasure;
typedef struct MyStruct1
{
	int x,y;

}POINT;
Treasure point[15];
int map[210][210];
int dis[15][15];
int n,m,k,to;
int dp[8500][15];
int vis[210][210];
int cost[210][210];
int add[4][2]={{1,0},{0,1},{-1,0},{0,-1}};
bool flag[210][210];

void bfs_0(int num)
{
	int i,j;
	int go_out=MAXN;
	memset(vis,0,sizeof(vis));
	for(i=1;i<=n;i++)
		for(j=1;j<=m;j++)
			cost[i][j]=MAXN;
	POINT temp,newd;
	temp.x=point[num].x;
	temp.y=point[num].y;
	cost[temp.x][temp.y]=0;
	vis[temp.x][temp.y]=1;
	queue<POINT>Q;
	Q.push(temp);
	while (!Q.empty())
	{
		temp=Q.front();
		Q.pop();
		vis[temp.x][temp.y]=0;
		for ( i = 0; i < 4; i++)
		{
			newd=temp;
			newd.x+=add[i][0];
			newd.y+=add[i][1];
			if(newd.x<1 || newd.x>n || newd.y<1 || newd.y>n)
			{				
				if(cost[temp.x][temp.y]<go_out)
				{go_out=cost[temp.x][temp.y];}
			}
			else if(map[newd.x][newd.y]!=-1)
			{
				if(cost[temp.x][temp.y]+map[newd.x][newd.y]<cost[newd.x][newd.y])
				{
					cost[newd.x][newd.y]=cost[temp.x][temp.y]+map[newd.x][newd.y];
					if(!vis[newd.x][newd.y])
					{
						Q.push(newd);
						vis[newd.x][newd.y]=1;
					}
				}
			}
		}
	}
	dis[0][num]=go_out+map[point[num].x][point[num].y];
	dis[num][to]=go_out;
}
int main()
{
	int t,i,j,s,temp,ans;
	scanf("%d",&t);
	while (t--)
	{
		memset(flag,0,sizeof(flag));
		scanf("%d%d",&n,&m);
		for(i=1;i<=n;i++)
			for(j=1;j<=m;j++)
				scanf("%d",&map[i][j]);
		scanf("%d",&k);to=k+1;
		for(i=1;i<=k;i++)
		{
			scanf("%d%d",&point[i].x,&point[i].y);
			point[i].x++;
			point[i].y++;
			flag[point[i].x][point[i].y]=true;
		}

		if(k==0)
		{
			printf("0\n");
			continue;
		}

		for ( i = 1; i <= k ; i++)
		{
			bfs_0(i);//计算i号宝藏距图边缘的距离
			for ( j = 1; j <=k ; j++)
			{
				dis[i][j]=cost[point[j].x][point[j].y];
			}
			dis[i][i]=map[point[i].x][point[i].y];
		}

		for(i=1;i<=(1<<k)-1;i++)
		{	
			for(j=1;j<=k;j++)
			{				
				if(i==(1<<(j-1)))dp[i][j]=dis[0][j];
				else dp[i][j]= MAXN;
			}
		}
		for ( i = 1; i <= (1<<k)-1; i++)
		{
			for ( j = 1; j <=k ; j++)
			{
				for ( s = 1; s <=k ; s++)
				{
					temp=i-(1<<(j-1));
					if (s!=j && (i&(1<<(j-1))) && (i&(1<<(s-1))) && dp[temp][s]!=MAXN)
					{
						if(dp[i][j]>dp[temp][s]+dis[s][j])
							dp[i][j]=dp[temp][s]+dis[s][j];
					}
				}
			}
		}

		ans=MAXN;
		for ( i = 1; i <=k; i++)
		{
			if(ans>dp[(1<<k)-1][i]+dis[i][to])
				ans=dp[(1<<k)-1][i]+dis[i][to];
		}
		printf("%d\n",ans);
	}
	return 0;
}
*/


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值