题意:
有
n
个点
思路:
首先一个明显的思路就是想办法转化成多起点多终点的最短路,然后建立一个超级起点和超级终点,跑一次得到答案。
但此题起点集和终点集都来自一个集合,所以只能想办法将集合进行分组。
考虑二进制分解,对于每两个不等的数,至少有一个二进制位不一样,这样我们可以枚举每一个二进制位,然后按
代码:
#include<cmath>
#include<queue>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
const ll INF = 1e15 + 10;
const int A = 1e5 + 10;
class Edge{ //存边
public:
int u,v;
ll w;
}edge[A<<1];
class Gra{ //存图
public:
int v,next;
ll w;
}G[A<<1];
ll dis[A];
bool vis[A];
int head[A],p[A],Belong[A];
int tot,n,m,k,Mx;
void add(int u,int v,ll w){
G[tot].v = v;
G[tot].w = w;
G[tot].next = head[u];
head[u] = tot++;
}
void build_G(){ //建图
memset(head,-1,sizeof(head));tot = 0;
for(int i=0 ;i<m ;i++){
int u = Belong[edge[i].u],v = Belong[edge[i].v];
add(u,v,edge[i].w);
}
}
ll Spfa(ll st,ll ed){ //跑最短路
queue<ll> que;
for(int i=0;i<=n+1 ;i++){
dis[i] = INF;vis[i] = 0;
}
que.push(st);
dis[st] = 0,vis[st] = 1;
while(que.size()){
int u = que.front();que.pop();
vis[u] = 0;
for(int i=head[u] ;i!=-1 ;i=G[i].next){
int v = G[i].v;ll w = G[i].w;
if(w + dis[u] < dis[v]){
dis[v] = w + dis[u];
if(!vis[v]){
vis[v] = 1;que.push(v);
}
}
}
}
return dis[ed];
}
void solve(){
for(int i=0 ;i<=n+1 ;i++) Belong[i] = i;
int S = 0,T = n+1; //建立超级起点和超级终点
ll ans = INF;
for(int i=0 ;(1<<i)<=Mx ;i++){
for(int j=0 ;j<k ;j++){
if((p[j]>>i)&1) Belong[p[j]] = S; //分组
else Belong[p[j]] = T;
}
build_G();
ans = min(ans,Spfa(S,T));
ans = min(ans,Spfa(T,S));
}
printf("%I64d\n",ans);
}
int main(){
int T,_=1;scanf("%d",&T);
while(T--){
scanf("%d%d",&n,&m);
for(int i=0 ;i<m ;i++){
scanf("%d%d%I64d",&edge[i].u,&edge[i].v,&edge[i].w);
}
scanf("%d",&k);Mx = 0;
for(int i=0 ;i<k ;i++){
scanf("%d",&p[i]);
Mx = max(Mx,p[i]);
}
printf("Case #%d: ",_++);
solve();
}
return 0;
}