这道题有很多解法,我是按照题解的做法写了一遍。
题意:给出一个有向图,然后有k个点,问这k个点两两之间的最短距离的最小值是多少。
解法:题解是把k个点按照每个点二进制位的不同分成两个集合,然后求一次多源最短路。由于最大的数是1e5,所以也就到个。。。2^17的样子?由于是有向图,还需要正反都跑一遍。所以一共跑34次最短路就出来了。。。
代码如下:
#include<iostream>
#include<cstdio>
#include<vector>
#include<queue>
#include<utility>
#include<stack>
#include<algorithm>
#include<cstring>
#include<string>
#include<cmath>
#include<set>
#include<map>
using namespace std;
typedef pair<long long, int> pii;
const int maxn = 1e5 + 5;
const int maxm = 2e5 + 5;
const long long INF = 1e18;
int a[maxn];
int head[maxn], to[maxm], nx[maxm], cost[maxm], ppp = 0;
void add_edge(int u, int v, int val) {
to[ppp] = v, nx[ppp] = head[u], cost[ppp] = val, head[u] = ppp++;
}
priority_queue <pii, vector<pii>, greater<pii> > q;
long long dis[maxn];
void dijkstra() {
while(!q.empty()) {
pii tmp = q.top();
q.pop();
int u = tmp.second;
if(dis[u] < tmp.first)
continue;
for(int i = head[u]; ~i; i = nx[i]) {
int v = to[i];
if(dis[v] > dis[u] + cost[i]) {
dis[v] = dis[u] + cost[i];
q.push(pii(dis[v], v));
}
}
}
}
int main() {
#ifndef ONLINE_JUDGE
freopen("in.txt", "r", stdin);
// freopen("out.txt", "w", stdout);
#endif
int T, Case = 1;
scanf("%d", &T);
while(T--) {
memset(head, -1, sizeof(head));
ppp = 0;
int n, m;
scanf("%d%d", &n, &m);
while(m--) {
int u, v, c;
scanf("%d%d%d", &u, &v, &c);
add_edge(u, v, c);
}
int k;
scanf("%d", &k);
for(int i = 0; i < k; i++) {
scanf("%d", &a[i]);
}
int ind = 1;
long long Min = 1e18;
while(1) {
int cnt = 0;
fill(dis, dis + n + 1, INF);
for(int i = 0; i < k; i++) {
if(a[i] & ind) {
dis[a[i]] = 0;
q.push(pii(0, a[i]));
cnt++;
}
}
if(cnt == 0)
break;
dijkstra();
for(int i = 0; i < k; i++) {
if(a[i] & ind)
continue;
if(Min > dis[a[i]])
Min = dis[a[i]];
}
fill(dis, dis + n + 1, INF);
for(int i = 0; i < k; i++) {
if(!(a[i] & ind)) {
dis[a[i]] = 0;
q.push(pii(0, a[i]));
cnt++;
}
}
dijkstra();
for(int i = 0; i < k; i++) {
if(!(a[i] & ind))
continue;
if(Min > dis[a[i]])
Min = dis[a[i]];
}
ind <<= 1;
}
printf("Case #%d: %lld\n", Case++, Min);
}
return 0;
}