题目:http://acm.hdu.edu.cn/showproblem.php?pid=4568
题意:给定一个矩阵图,每走一个格子有一个花费,可以从任意边进入,任意边出去,并且图中有k个点要求全部走到,问最少的花费是多少,如果不能够全部走到,输出0。
思路:先用spfa对图做预处理,求出所有必须走的点和地图外构成的图的任意两点的最短路,然后就是枚举必须走的点的访问顺序问题,由于k很小,可以用状态压缩DP,dp[i][j]表示最后选的点是i,j表示已经选的点。
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;
const int inf=2099999999;
int s[205][205],ma[205][205];
int dis[15][15],c[15];
int dir[4][2]={{0,1},{0,-1},{1,0},{-1,0}};
int n,m;
int f[15];
int dp[15][1<<14];
int d[40005],vis[40005];
struct node{
int di,u;
bool operator <(const node& rhs) const{
return di>rhs.di;
}
};
void spfa(int st,int num){
int i;
priority_queue<node> q;
for(i=0;i<n*m;i++) d[i]=inf;
d[st]=0;
memset(vis,0,sizeof(vis));
q.push((node){0,st});
while(!q.empty()){
node p=q.top();
q.pop();
int u=p.u;
if(vis[u]) continue;
vis[u]=1;
int x,y,xx,yy,xu;
x=u/m,y=u%m;
if(ma[x][y]>-1) dis[num][ma[x][y]]=d[u];
if(x==0||x==n-1||y==0||y==m-1) c[num]=min(c[num],d[u]);
for(i=0;i<4;i++){
xx=x+dir[i][0];
yy=y+dir[i][1];
if(s[xx][yy]==-1) continue;
if(xx>n-1||xx<0||yy>m-1||yy<0) continue;
xu=xx*m+yy;
if(d[xu]>d[u]+s[xx][yy]){
d[xu]=d[u]+s[xx][yy];
q.push((node){d[xu],xu});
}
}
}
}
int main(){
int i,j,k,K,x,y,state;
int t;
scanf("%d",&t);
while(t--){
scanf("%d%d",&n,&m);
for(i=0;i<n;i++)
for(j=0;j<m;j++)
scanf("%d",&s[i][j]);
memset(ma,-1,sizeof(ma));
memset(dp,0x7f,sizeof(dp));
memset(dis,0,sizeof(dis));
scanf("%d",&K);
for(i=0;i<K;i++){
int a,b;
scanf("%d%d",&a,&b);
ma[a][b]=i;
f[i]=a*m+b;
c[i]=inf;
}
for(i=0;i<K;i++) spfa(f[i],i);
for(i=0;i<K;i++){
x=f[i]/m,y=f[i]%m;
dp[i][1<<i]=c[i]+s[x][y];
}
for(i=1;i<(1<<K);i++){
for(j=0;j<K;j++){
for(k=0;k<K;k++){
if((i&(1<<j))&&(i&(1<<k))&&j!=k){
if(dp[j][i^(1<<k)]!=inf) dp[k][i] = min(dp[k][i], dp[j][i^(1<<k)]+dis[j][k]);
}
}
}
}
int ans=inf;
for(i=0;i<K;i++)
ans=min(ans,dp[i][(1<<K)-1]+c[i]);
printf("%d\n",ans);
}
return 0;
}