模型:给一个n个点的无向带权图,求从指定起点出发,经过指定的k个点(顺序不定)最后回到起点的最小代价。
数据范围:n<=10^4, m<=10^5, k<15
分析:先求最短路,求出从指定的k+1(含起点)个点到其他点的最小代价,然后建立k+1个点之间的最小代价邻接矩阵。最后用状态压缩DP求结果(状态压缩DP参考上一篇)。
#include <stdio.h> #include <string.h> #include <queue> using namespace std; #define MIN(a,b) ((a)<(b)?(a):(b)) #define INF 0x3f3f3f3f #define N 10010 #define M 200010 #define K 15 int n,m,k,e; int first[N],next[M],v[M],w[M]; int id[K]; int d[K][N]; bool inq[N]; int g[K][K]; int dp[1<<K][K]; void init() { e=0; memset(first,-1,sizeof(first)); } void add(int a,int b,int c) { v[e]=b; w[e]=c; next[e]=first[a]; first[a]=e++; } void spfa(int i) { int a,b,j; a=id[i]; queue<int>q; memset(inq,0,sizeof(inq)); memset(d[i],0x3f,sizeof(d[i])); d[i][a]=0; q.push(a); inq[a]=1; while(!q.empty()) { a=q.front(),q.pop(); inq[a]=0; for(j=first[a];j!=-1;j=next[j]) { b=v[j]; if(d[i][b]>d[i][a]+w[j]) { d[i][b]=d[i][a]+w[j]; if(!inq[b]) inq[b]=1,q.push(b); } } } } int DP(int s,int last) { if(dp[s][last]!=-1) return dp[s][last]; int ns=s^(1<<last); if(ns==1) return g[0][last]; int ret=INF; for(int i=1;i<k;i++) if((ns>>i)&1) { ret=MIN(ret,DP(ns,i)+g[i][last]); } return dp[s][last]=ret; } int main() { int i,j,a,b,c; while(~scanf("%d%d%d",&n,&m,&k)) { init(); while(m--) { scanf("%d%d%d",&a,&b,&c); add(a,b,c); add(b,a,c); } k++; for(i=0;i<k;i++) { scanf("%d",&id[i]); spfa(i); } for(i=0;i<k;i++) { for(j=0;j<k;j++) { g[i][j]=d[i][id[j]]; } } int ans=INF; memset(dp,-1,sizeof(dp)); for(i=0;i<k;i++) { ans=MIN(ans,DP((1<<k)-1,i)+g[0][i]); } if(ans<INF) printf("%d\n",ans); else puts("What a pity"); } return 0; }