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.
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.
![](https://i-blog.csdnimg.cn/blog_migrate/0f3257bc5d888f8c799eb1c5e99e8ed5.jpeg)
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;
}