浙江省赛F (×) hdu6166(√) k个点之间求最短路径(多源最短路)
问题描述
给定n个点,m条有向边,选k个点求他们两两之间的最短路径。
前置知识
- 集合间的最短路径:选定一个集合,将其所有点的
dis[i]设置为0(相当于将集合视为一个超级源点),然后执行Dijkstra算法求出dis数组。对于不在集合内的点x,最小的dis[x]即为两个集合间的最短路径。
解题思路
-
二进制分组思想:
- 每个点的二进制表示至少有一位不同。
- 按每个二进制位将点分为两组(该位为0和该位为1)。
- 对每个二进制位,分别计算两组之间的最短路径。
-
关键观察:
- 任意两个点至少在一个二进制位上不同,因此它们的路径会被包含在某个二进制位的分组计算中。
- 最终结果为所有二进制位分组计算的最小值。
-
注意事项:
- 有向边需要分别以每组作为超级源点执行Dijkstra。
- 使用虚拟节点
0和n+1简化集合间距离的计算。
代码实现
#include<bits/stdc++.h>
#define int long long
using namespace std;
// Debugging macros
#define dbg(x...) \
do { \
cout << #x << " -> "; \
err(x); \
} while (0)
void err() {
cout << endl << endl;
}
template<class T, class... Ts>
void err(T arg, Ts ... args) {
cout << fixed << setprecision(10) << arg << ' ';
err(args...);
}
const int maxn = 1e5 + 10;
struct node {
int v, w;
bool operator < (const node & b) const {
return w > b.w;
}
};
vector<node> g[maxn];
vector<tuple<int, int, int>> pi;
int dis[maxn], vis[maxn];
int T, n, m, ttt;
void dij() {
for (int i = 0; i <= n + 1; i++) dis[i] = 1e18, vis[i] = 0;
dis[0] = 0;
priority_queue<node> q;
q.push({0, 0});
while (!q.empty()) {
auto [u, ww] = q.top();
q.pop();
if (vis[u]) continue;
vis[u] = 1;
for (auto [v, w] : g[u]) {
if (dis[v] > dis[u] + w) {
dis[v] = dis[u] + w;
q.push({v, dis[v]});
}
}
}
}
void solve() {
ttt++;
cin >> n >> m;
pi.clear();
for (int i = 1; i <= m; i++) {
int u, v, w; cin >> u >> v >> w;
pi.push_back({u, v, w});
}
int k; cin >> k;
vector<int> a(k + 1);
for (int i = 1; i <= k; i++) {
cin >> a[i];
}
int mi = 1e18;
for (int i = 20; i >= 0; i--) {
// Case 1: Group with bit i = 1 as source
for (int j = 0; j <= n + 1; j++) g[j].clear();
for (int j = 1; j <= m; j++) {
auto [u, v, w] = pi[j - 1];
g[u].push_back({v, w});
}
for (int j = 1; j <= k; j++) {
int x = a[j];
if ((x >> i) & 1) {
g[0].push_back({x, 0});
} else {
g[x].push_back({n + 1, 0});
}
}
dij();
mi = min(mi, dis[n + 1]);
// Case 2: Group with bit i = 0 as source
for (int j = 0; j <= n + 1; j++) g[j].clear();
for (int j = 1; j <= m; j++) {
auto [u, v, w] = pi[j - 1];
g[u].push_back({v, w});
}
for (int j = 1; j <= k; j++) {
int x = a[j];
if ((x >> i) & 1) {
g[x].push_back({n + 1, 0});
} else {
g[0].push_back({x, 0});
}
}
dij();
mi = min(mi, dis[n + 1]);
}
assert(mi != 1e18);
cout << "Case #" << ttt << ": " << mi << endl;
}
signed main() {
ios::sync_with_stdio(false);
cin.tie(0);
T = 1;
cin >> T;
while (T--) solve();
return 0;
}

浙公网安备 33010602011771号