样例输入:
1
5 6
1 2 1
2 3 3
3 1 3
2 5 1
2 4 2
4 3 1
3
1 3 5
样例输出:
2
数据范围:
对于 30% 的数据,K≤4;
对于另外 10% 的数据,K=N;
对于另外 30% 的数据,M=N-1;
对于 100% 的数据,1≤N,M≤100000;T≤5;1≤K≤n;1≤边长≤100000。
题目分析:
我们可以将这k个点分成两部分,一部分点同时作为起点,一部分点同时作为终点,然后求一次从起点集合到终点集合的最短路,就可以找出最短的路径。但因为实际上的最短路径可能存在于起点集合或终点集合中。因为它们是不同的点,所以二进制位不可能全部相同,于是我们每次依据不同位,将是0的和是1的分成两部分,执行上述操作。于是各次最短路的最小值就是答案。
附代码:
#include<iostream>
#include<cstdlib>
#include<cstdio>
#include<ctime>
#include<cmath>
#include<cstring>
#include<string>
#include<cctype>
#include<iomanip>
#include<algorithm>
#include<queue>
using namespace std;
const int N=1e5+100;
const int INF=0x3f3f3f3f;
int first[N],to[N*2],nxt[N*2],w[N*2];
int t,n,m,x,y,z,k,ans,tot,des[N],num[N],dis[N];
priority_queue<pair<int,int> >q;
bool check[N];
int readint()
{
char ch;int i=0,f=1;
for(ch=getchar();(ch<'0'||ch>'9')&&ch!='-';ch=getchar());
if(ch=='-') {ch=getchar();f=-1;}
for(;ch>='0'&&ch<='9';ch=getchar()) i=(i<<3)+(i<<1)+ch-'0';
return i*f;
}
void create(int x,int y,int z)
{
tot++;
nxt[tot]=first[x];
first[x]=tot;
to[tot]=y;
w[tot]=z;
}
void dijkstra(int po)// dijkstra+优先队列求最短路
{
while(!q.empty())
{
int u=q.top().second;
q.pop();
if(check[u]==true)//第一次找到的终点集合的点必然就是当前的最短路
{
ans=min(ans,dis[u]);
return;
}
for(int e=first[u];e;e=nxt[e])
{
int v=to[e];
if(dis[v]>dis[u]+w[e])
{
dis[v]=dis[u]+w[e];
q.push(make_pair(-dis[v],v));
}
}
}
}
void find()
{
for(int i=0;(1<<i)<=k;i++)
{
memset(dis,INF,sizeof(dis));
memset(check,0,sizeof(check));
while(!q.empty()) q.pop();//因为dijkstra中有break操作,所以需要清空
for(int j=1;j<=k;j++)
if(!(j&(1<<i)))//二进制此位为0的作为起点
{
dis[num[j]]=0;//都把dis赋成0
q.push(make_pair(0,num[j]));
}
else check[num[j]]=true;//标记终点
dijkstra(i);
}
}
int main()
{
//freopen("path.in","r",stdin);
//freopen("path.out","w",stdout);
t=readint();
while(t--)
{
tot=0;ans=INF;
memset(first,0,sizeof(first));
memset(num,0,sizeof(num));
n=readint();m=readint();
for(int i=1;i<=m;i++)
{
x=readint();y=readint();z=readint();
create(x,y,z);
create(y,x,z);
}
k=readint();
for(int i=1;i<=k;i++)
{
des[i]=readint();
num[i]=des[i];//实际给每个点编号1~k,依据这个编号分,每次就只用枚举1~k
}
find();
printf("%d\n",ans);
}
return 0;
}