题目传送门:http://acm.hdu.edu.cn/showproblem.php?pid=4568
题意:给你一个四联通的长方形,每一个格子中都有一个值,你走到这个格子要耗费这些体力,-1代表无法通过,其中有一些格子里面有宝藏,一个人可以从边界的任意一个点进入,边界的任意一个点出去,问如果他得到了所有的宝藏,耗费的体力最少是多少。
分析:总共有40000个格子,对这40000个格子分析显然不可能,于是先用Dijstra将有宝藏的格子之间的最短路算出来,然后这些格子都要访问到,现在要确定访问顺序,当格子数在10以下的时候,可以使用暴力搜索,时间复杂度是n!,但是题目中给了宝藏数最多为13,于是不能爆搜,就要用到状压DP,dp[i][j],i为用二进制保存每个宝藏是否被取得,j为最后一个走到的是哪一个宝藏,然后就可以得到dp[i][j]=min(dp[i][j],dp[i-(1<<j)][k]+g[k][j]);j是当前走的格子,k为上一个,在走第一个宝藏的时候要加上边界到这个格子的距离,最后一个同理。
代码:
#include <bits/stdc++.h>
using namespace std;
const int MAXN=40100;
struct qnode
{
int v;
int c;
qnode(int _v=0,int _c=0):v(_v),c(_c) {}
bool operator <(const qnode &r)const
{
return c>r.c;
}
};
struct Edge
{
int v,cost;
Edge(int _v=0,int _cost=0):v(_v),cost(_cost) {}
};
int m,n;
int T,x,y,xx1,yy1,a[205][205],ge,wei[15],g[15][15],dp[1<<13][15],ans;
int r[4]={0,0,1,-1},c[4]={1,-1,0,0};
vector<Edge> E[MAXN];
bool vis[MAXN];
int dist[MAXN];
void Dijkstra(int len,int start)
{
memset(vis,0,sizeof(vis));
memset(dist,0x3f,sizeof(dist));
priority_queue<qnode>que;
while(!que.empty())
que.pop();
dist[start]=0;
que.push(qnode(start,0));
qnode tmp;
while(!que.empty())
{
tmp=que.top();
que.pop();
int u=tmp.v;
if(vis[u])
continue;
vis[u]=true;
for(int i=0; i<E[u].size(); i++)
{
int v=E[tmp.v][i].v;
int cost=E[u][i].cost;
if(!vis[v]&&dist[v]>dist[u]+cost)
{
dist[v]=dist[u]+cost;
que.push(qnode(v,dist[v]));
}
}
}
}
void addedge(int u,int v,int w)
{
E[u].push_back(Edge(v,w));
}
void init()
{
for(int i=0;i<=n;i++)
E[i].clear();
}
int js(int h1,int h2)
{
return h1*y+h2;
}
int main()
{
ios::sync_with_stdio(false);
cin>>T;
while(T--)
{
cin>>x>>y;
n=x*y;
init();
for(int i=0;i<x;i++)
for(int j=0;j<y;j++)
cin>>a[i][j];
for(int i=0;i<x;i++)
{
for(int j=0;j<y;j++)
{
for(int k=0;k<4;k++)
{
xx1=i+r[k];
yy1=j+c[k];
if(xx1>=0&&xx1<x&&yy1>=0&&yy1<y&&a[xx1][yy1]!=-1)
addedge(js(i,j),js(xx1,yy1),a[xx1][yy1]);
}
}
}
cin>>ge;
for(int i=0;i<ge;i++)
{
cin>>xx1>>yy1;
wei[i]=js(xx1,yy1);
}
for(int i=0;i<ge;i++)
{
Dijkstra(n,wei[i]);
for(int j=0;j<ge;j++)
g[i][j]=dist[wei[j]];
}
for(int i=0;i<x;i++)
for(int j=0;j<y;j++)
if((i==0||j==0||i==x-1||j==y-1)&&a[i][j]!=-1)
addedge(n,js(i,j),a[i][j]);
for(int i=0;i<ge;i++)
{
Dijkstra(n+1,n);
g[ge][i]=dist[wei[i]];
g[i][ge]=dist[wei[i]]-a[wei[i]/y][wei[i]%y];
}
memset(dp,0x3f,sizeof(dp));
for(int i=0;i<ge;i++)
dp[1<<i][i]=g[ge][i];
int s=1<<ge;
for(int i=1;i<s;i++)
for(int j=0;j<ge;j++)
if((i>>j)&1)
for(int k=0;k<ge;k++)
if(k!=j&&(i>>k)&1)
dp[i][j]=min(dp[i][j],dp[i-(1<<j)][k]+g[k][j]);
ans=1e8;
for(int i=0;i<ge;i++)
ans=min(ans,dp[s-1][i]+g[i][ge]);
if(ans==1e8)
cout<<0<<endl;
else
cout<<ans<<endl;
}
return 0;
}