浙江省赛F (×) hdu6166(√) k个点之间求最短路径(多源最短路)

问题描述

给定n个点,m条有向边,选k个点求他们两两之间的最短路径。

前置知识

  • 集合间的最短路径:选定一个集合,将其所有点的dis[i]设置为0(相当于将集合视为一个超级源点),然后执行Dijkstra算法求出dis数组。对于不在集合内的点x,最小的dis[x]即为两个集合间的最短路径。

解题思路

  1. 二进制分组思想

    • 每个点的二进制表示至少有一位不同。
    • 按每个二进制位将点分为两组(该位为0和该位为1)。
    • 对每个二进制位,分别计算两组之间的最短路径。
  2. 关键观察

    • 任意两个点至少在一个二进制位上不同,因此它们的路径会被包含在某个二进制位的分组计算中。
    • 最终结果为所有二进制位分组计算的最小值。
  3. 注意事项

    • 有向边需要分别以每组作为超级源点执行Dijkstra。
    • 使用虚拟节点0n+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;
}
posted @ 2025-04-26 23:04  sword1e1  阅读(33)  评论(0)    收藏  举报