这一题我们从 n <=17 , 而且无法用贪心确定三个人该如何去选择路径,就可得出这一题应该是:状压 dp。
我们用二进制状态 sta 表示走过那些点,1 表示已经走过改点,0 表示还没被走过。
假设 f [sta][x] 编号从 1 号节点出发走过 sta 状态,并最终走到 x 节点的最短路径距离。
f [sta][x] 的可以用状压 dp 的思路求解,只不过因为给的是一个无向图,因此我们的状态转移可以使用 spfa 在求最短路、遍历图的时候去进行状态转移,这里要注意 spfa 中的标记数组要开成这样: vis [sta][x] ,因为无向图中的点是可以被一个人重复走多次的!!!
有了 f 数组之后,其实就是从 f 中选择三个不相交的 二进制状态,这些二进制状态的并 要覆盖我们要到达的 k 个点,并且这个三个二进制状态 索要走的最短距离和就是我们的答案。
那么怎么选择状态呢?其实 用 dp 背包的思想,把要选择的状态当成 物品 去暴力枚举选择就行了!
代码
#include<bits/stdc++.h>
using namespace std;#definedbdouble#definelllonglong#definescscanf#defineprprintf#definefifirst#definesesecond#definepbpush_back#definem_pmake_pair#definePirpair<int,int>#defineinf0x3f3f3f3f#defineINF0x3f3f3f3f3f3f3f3f/*==========ACMer===========*/constint N =20;int n, m, k, S, statu;int mz[N][N];int f[N][1<<17];int dp[4][1<<17];int vis[N][1<<17];voidinit(){
S =(1<< n)-1;for(int i =0; i < n; i ++)for(int j =0; j < n; j ++)
mz[i][j]= i == j ?0: inf;}voidspfa(){
queue<Pir> q;for(int i =0; i < n; i ++)for(int j =0; j <= S; j ++)
f[i][j]= inf, vis[i][j]=0;
f[0][1]=0;
q.push(m_p(0,1));while(q.size()){
Pir now = q.front(); q.pop();int u = now.fi, s = now.se;
vis[u][s]= false;for(int v =0; v < n; v ++){if(mz[u][v]== inf)continue;if(f[v][s |(1<< v)]> f[u][s]+ mz[u][v]){
f[v][s |(1<< v)]= f[u][s]+ mz[u][v];if(! vis[v][s |(1<< v)]){
vis[v][s |(1<< v)]= true;
q.push(m_p(v, s |(1<< v)));}}}}}intsolve(){for(int i =1; i <=3; i ++)for(int j =0; j <= S; j ++)
dp[i][j]= inf;for(int s =0; s <= S; s ++)for(int i =0; i < n; i ++)
dp[1][s]=min(dp[1][s], f[i][s]);for(int i =2; i <=3; i ++)for(int j =0; j <= S; j ++)for(int k = j; k; k = j &(k -1))
dp[i][j]=min(dp[i][j],max(dp[1][k], dp[i -1][(j ^ k)+1]));//异或之后别忘了+1 因为每次都是从 0节点 出发的int ans = inf;for(int i =1; i <=3; i ++)for(int j =0; j <= S; j ++)if((j & statu)== statu)
ans =min(ans, dp[i][j]);return ans == inf ?-1: ans;}intmain(){int T, cas =1;sc("%d",&T);while(T --){sc("%d %d",&n,&m);int u, v, w;init();for(int i =1; i <= m; i ++){sc("%d %d %d",&u,&v,&w);
u --, v --;
mz[u][v]= mz[v][u]=min(mz[u][v], w);}sc("%d",&k);
statu =0;for(int i =1; i <= k; i ++){sc("%d",&u);
statu |=(1<<(u -1));}spfa();pr("Case %d: %d\n", cas ++,solve());}return0;}