http://acm.hdu.edu.cn/showproblem.php?pid=6166
题意:给你一幅有向图,选取 K 个点,k 个点任意二点做最短路,输出最小值。
题解:根据k个点某一位上的二进制是 0 或是 1 ,分为二个集合,一个集合为源点,另一个集合为汇点。
最小值其实就是 k 个点中某二个点的最短路,将这二个点分别放入一个集合,剩余的 k-2 个点放任意集合即可。
根据某一位上的二进制划分,可以将任意二点分开。
多源多汇点求最短路。由于二进制的位数最多 20 位,所以做 20 次dijkstra即可。
由于这是有向图,分点的时候源点、汇点调换一下。
这里的边用链式向前星存储,dijkstra用优先队列优化。
#include<cstdio> #include<algorithm> #include<iostream> #include<cstring> #include<vector> #include<queue> #define oo 0x3f3f3f3f using namespace std; const int MAXN = 100000+10; struct node { int b, e, w; int next; }edge[MAXN]; struct no { int e, w; no(){ } no(int e, int w):e(e), w(w){ } bool operator < (const no aa)const { return w > aa.w; } }; int head[MAXN]; int a[MAXN]; bool ise[MAXN]; int grap[MAXN]; priority_queue<no> que; int dij() { while(!que.empty()) { no nod = que.top(); que.pop(); if(ise[nod.e]) return nod.w; for(int i = head[nod.e]; ~i; i = edge[i].next) { if(grap[edge[i].e] > grap[nod.e] + edge[i].w) { grap[edge[i].e] = grap[nod.e] + edge[i].w; que.push( no(edge[i].e, grap[edge[i].e]) ); } } } return oo; } void addedge(int x, int y, int z, int i) { edge[i].b = x; edge[i].e = y; edge[i].w = z; edge[i].next = head[x]; head[x] = i; } void init() { memset(ise, false, sizeof(ise)); memset(grap, oo, sizeof(grap)); while(!que.empty()) que.pop(); } int main (void) { ios::sync_with_stdio(false); int t; cin >> t; int Case = 0; while(t--) { int n, m; cin >> n >> m; memset(head, 0xffff, sizeof(head)); for(int i = 1; i <= m; i++) { int x, y, z; cin >> x >> y >> z; addedge(x, y, z, i); } int k; cin >> k; for(int i = 1; i <= k; i++) { cin >> a[i]; } int ans = oo; for(int j = 0; j <= 20; j++ ) { init(); for(int i = 1; i <= k; i++) { if(a[i]&(1<<j)) { ise[a[i]] = true; } else { que.push(no(a[i], 0)); grap[a[i]] = 0; } } ans = min(ans, dij()); init(); for(int i = 1; i <= k; i++) { if(a[i]&(1<<j)) { que.push(no(a[i], 0)); grap[a[i]] = 0; } else { ise[a[i]] = true; } } ans = min(ans, dij()); } cout << "Case #" << ++Case << ": " << ans << endl; } } /* 1 2 1 1 2 1 2 1 2 */