一个N*M的迷宫,每个点有代价,代价为-1时表示不能走到,迷宫中有k个宝藏,求取走所有宝藏所需要的最小代价,只能进入迷宫一次
计算出所有宝藏之间的最短距离及从该宝藏出迷宫的最短距离,然后做状压DP即可
#include "stdio.h"
#include "string.h"
#include "queue"
using namespace std;
struct node
{
int x,y,cost;
bool friend operator<(node n1,node n2)
{
return n2.cost<n1.cost;
}
};
const int inf=0x3f3f3f3f;
const int dir[4][2]={ {1,0},{-1,0},{0,1},{0,-1} };
int n,m,k,tot;
int dis[20][20],mark[210][210],a[210][210],b[20],x[20],y[20]; // mark记录点是否存在宝藏并记录编号,dis记录宝藏间最短距离
int used[210][210],dp[70000][20]; // dp[i][j]=i状态,以j点为最终结点的最小代价
int Min(int a,int b)
{
if (a<b) return a;
else return b;
}
int judge(int x,int y)
{
if (x<0 || y<0 || x>=n || y>=m) return 0;
if (used[x][y]!=0) return 0;
if (a[x][y]==-1) return 0;
return 1;
}
void bfs(int w)
{
priority_queue<node>q;
node cur,next;
int i;
memset(used,0,sizeof(used));
cur.x=x[w];
cur.y=y[w];
cur.cost=0;
if (cur.x==0 || cur.y==0 || cur.x==n-1 || cur.y==m-1) // 是否在边界,在边界则记录到0号点距离
dis[w][0]=dis[0][w]=dis[w][k+1]=dis[k+1][w]=0;
used[cur.x][cur.y]=1;
q.push(cur);
while (!q.empty())
{
cur=q.top();
q.pop();
for (i=0;i<4;i++)
{
next.x=cur.x+dir[i][0];
next.y=cur.y+dir[i][1];
if (judge(next.x,next.y)==0) continue;
next.cost=cur.cost+a[next.x][next.y];
used[next.x][next.y]=1;
if (next.x==0 || next.y==0 || next.x==n-1 || next.y==m-1)
{
dis[w][0]=dis[0][w]=Min(dis[w][0],next.cost);
dis[w][k+1]=dis[k+1][w]=dis[w][0];
}
if (mark[next.x][next.y]!=0)
{
dis[w][mark[next.x][next.y]]=Min(dis[w][mark[next.x][next.y]],next.cost-a[next.x][next.y]);
dis[mark[next.x][next.y]][w]=dis[w][mark[next.x][next.y]];
}
q.push(next);
}
}
}
void DP()
{
int i,j,l;
memset(dp,inf,sizeof(dp));
dp[1][0]=0;
for (i=0;i<=tot;i++)
for (j=0;j<=k+1;j++)
if ( (b[j]&i)!=0 && dp[i][j]!=-1)
for (l=0;l<=k+1;l++)
if (l!=j && (b[l]&i)==0)
dp[i+b[l]][l]=Min(dp[i+b[l]][l],dp[i][j]+dis[j][l]);
}
int main()
{
int t,i,j,ans;
b[0]=1;
for (i=1;i<=16;i++)
b[i]=b[i-1]*2;
scanf("%d",&t);
while (t--)
{
scanf("%d%d",&n,&m);
for (i=0;i<n;i++)
for (j=0;j<m;j++)
scanf("%d",&a[i][j]);
scanf("%d",&k);
memset(mark,0,sizeof(mark));
for (i=1;i<=k;i++)
{
scanf("%d%d",&x[i],&y[i]);
mark[x[i]][y[i]]=i;
}
memset(dis,inf,sizeof(dis));
for (i=1;i<=k;i++)
bfs(i); // 计算所有宝藏间的最短距离及与边界的最短距离
tot=b[k+2]-1;
DP(); // 状压DP
ans=dp[tot][k+1];
for (i=1;i<=k;i++)
ans+=a[x[i]][y[i]];
printf("%d\n",ans);
}
return 0;
}
版权声明:本文为博主原创文章,未经博主允许不得转载。
原文:http://blog.csdn.net/u011932355/article/details/47057135