• 最短路径树定义、性质、模板及例题


    最短路径树的定义

    给定一个无向连通带权图\(G = (V, E)\),节点\(u\)的最短路径树可以定义为:

    一个图\(G\)的生成树\(G_1 = (V, E_1)\),其中\(E_1\)\(E\)的子集。在\(G_1\)中从点\(u\)到其他任何点的最短距离与在\(G\)中相同。

    跑一遍Dijkstra算法,使用数组\(pre\)记录每个点是有哪条边更新的。

    性质

    • 根节点到其他所有点的最短距离与原图中相同
      证明:定义

    • 在所有生成树中,最短路径树满足根节点到其他所有点的距离之和最短(Atcoder ABC252 E)
      证明:由于根节点到其他任何点的距离都是原图的最短距离,因此距离之和也一定是最短的。

    模板

    void dijkstra()
    {
        priority_queue<pair<long long, int>, vector<pair<long long, int>>, greater<pair<long long, int>> > heap;
        for(int i = 1; i <= n; i ++) d[i] = 1e18;
        heap.push({0, 1});
        d[1] = 0;
        while(heap.size()) {
            auto t = heap.top();
            heap.pop();
            int ver = t.second;
            ll dist = t.first;
            if(st[ver]) continue;
            st[ver] = true;
            for(int i = h[ver]; ~i; i = ne[i]) {
                int j = e[i];
                if(d[j] > dist + w[i]) {
                    d[j] = dist + w[i];
                    heap.push({d[j], j});
                    pre[j] = i;
                }
            }
        }
    }
    

    例题

    1. CF545E Paths and Trees(权值和最小的最短路径树)
      思路:在更新的时候,如果d[j] = dist + w[i],则用权值最小的边来更新
    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #include <queue>
    
    using namespace std;
    
    typedef long long ll;
    typedef pair<ll, int> pii;
    
    const int N = 300010, M = 2 * N;
    
    int n, m, u;
    int h[N], e[M], ne[M], idx;
    ll w[M], d[N];
    int pre[N];
    bool st[N], isin[M];
    
    void add(int a, int b, ll c)
    {
        e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx ++;
    }
    
    void dijkstra(int u)
    {
        priority_queue<pii, vector<pii>, greater<pii> > heap;
        heap.push({0, u});
        for(int i = 1; i <= n; i ++) d[i] = 1e18;
        d[u] = 0;
        while(heap.size()) {
            auto t = heap.top();
            heap.pop();
            int ver = t.second;
            ll dist = t.first;
            if(st[ver]) continue;
            st[ver] = true;
            for(int i = h[ver]; ~i; i = ne[i]) {
                int j = e[i];
                if(d[j] > dist + w[i]) {
                    d[j] = dist + w[i];
                    heap.push({d[j], j});
                    pre[j] = i;
                }
                else if(d[j] == dist + w[i]) {
                    if(w[i] < w[pre[j]]) {
                        pre[j] = i;
                    }
                }
            }
        }
    }
    
    int main()
    {
        scanf("%d%d", &n, &m);
        memset(h, -1, sizeof h);
        for(int i = 0; i < m; i ++) {
            int a, b;
            ll c;
            scanf("%d%d%lld", &a, &b, &c);
            add(a, b, c), add(b, a, c);
        }
        scanf("%d", &u);
        dijkstra(u);
        ll ans = 0;
        for(int i = 1; i <= n; i ++) {
            if(i != u) {
                ans += w[pre[i]];
            }
        }
        printf("%lld\n", ans);
        for(int i = 1; i <= n; i ++) {
            if(i != u) {
                isin[pre[i] / 2] = true;
            }
        }
        for(int i = 0; i < m; i ++) {
            if(isin[i]) {
                printf("%d ", i + 1);
            }
        }
        printf("\n");
        return 0;
    }
    
    1. Acwing349 黑暗城堡(最短路径树计数)
      思路:对每个点可选边的数量计数。在跑Dijkstra算法的时候,如果d[j] = dist + w[i],那么当前点可选边数量+1;如果d[j] > dist + w[i],说明边权需要更新,则当前点可选边数量归1。
    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #include <queue>
    
    using namespace std;
    
    typedef long long ll;
    typedef pair<ll, int> pii;
    
    const int N = 1010, M = N * N;
    const ll mod = 2147483647;
    
    int n, m;
    int h[N], e[M], ne[M], idx;
    ll w[M], d[N], cnt[N];
    bool st[N];
    
    void add(int a, int b, ll c)
    {
        e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx ++;
    }
    
    void dijkstra()
    {
        priority_queue<pii, vector<pii>, greater<pii> > heap;
        heap.push({0, 1});
        for(int i = 1; i <= n; i ++) d[i] = 1e18;
        d[1] = 0;
        while(heap.size()) {
            auto t = heap.top();
            heap.pop();
            int ver = t.second;
            ll dist = t.first;
            if(st[ver]) continue;
            st[ver] = true;
            for(int i = h[ver]; ~i; i = ne[i]) {
                int j = e[i];
                if(d[j] > dist + w[i]) {
                    d[j] = dist + w[i];
                    heap.push({d[j], j});
                    cnt[j] = 1;
                }
                else if(d[j] == dist + w[i]) {
                    cnt[j] ++;
                }
            }
        }
    }
    
    int main()
    {
        scanf("%d%d", &n, &m);
        memset(h, -1, sizeof h);
        for(int i = 0; i < m; i ++) {
            int a, b;
            ll c;
            scanf("%d%d%lld", &a, &b, &c);
            add(a, b, c), add(b, a, c);
        }
        dijkstra();
        ll ans = 1;
        for(int i = 2; i <= n; i ++) {
            ans = ans * cnt[i] % mod;
        }
        printf("%lld\n", ans);
        return 0;
    }
    
    1. CF1005F Berland and the Shortest Paths(输出k条最短路径树的方案)
      思路:记录每个点的可选边。Dijkstra之后,通过dfs枚举方案。
    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #include <queue>
    #include <vector>
    
    using namespace std;
    
    typedef long long ll;
    typedef pair<int, int> pii;
    
    const int N = 200010, M = 2 * N;
    
    int n, m, k;
    int h[N], e[M], ne[M], idx;
    bool st[N];
    int d[N];
    vector<int> pre[N];
    int ans[M];
    
    void add(int a, int b)
    {
        e[idx] = b, ne[idx] = h[a], h[a] = idx ++;
    }
    
    void dijkstra()
    {
        priority_queue<pii, vector<pii>, greater<pii> > heap;
        heap.push({0, 1});
        for(int i = 1; i <= n; i ++) d[i] = 1e9;
        d[1] = 0;
        while(heap.size()) {
            auto t = heap.top();
            heap.pop();
            int ver = t.second, dist = t.first;
            if(st[ver]) continue;
            st[ver] = true;
            for(int i = h[ver]; ~i; i = ne[i]) {
                int j = e[i];
                if(d[j] > dist + 1) {
                    d[j] = dist + 1;
                    pre[j].clear();
                    pre[j].push_back(i);
                    heap.push({d[j], j});
                }
                else if(d[j] == dist + 1) {
                    pre[j].push_back(i);
                }
            }
        }
    }
    
    void dfs(int u)
    {
        if(u > n) {
            if(!k) exit(0);
            for(int i = 0; i < m; i ++) {
                printf("%d", ans[i]);
            }
            printf("\n");
            k --;
            return;
        }
        for(auto t : pre[u]) {
            ans[t / 2] = 1;
            dfs(u + 1);
            ans[t / 2] = 0;
        }
    }
    
    int main()
    {
        scanf("%d%d%d", &n, &m, &k);
        memset(h, -1, sizeof h);
        for(int i = 0; i < m; i ++) {
            int a, b;
            scanf("%d%d", &a, &b);
            add(a, b), add(b, a);
        }
        dijkstra();
        ll num = 1;
        for(int i = 2; i <= n; i ++) {
            num = num * (ll)pre[i].size();
            if(num > k) {
                num = k;
                break;
            }
        }
        k = num;
        printf("%d\n", k);
        dfs(2);
        return 0;
    }
    
    1. CF1076D Edge Deletion
      题意:给定一个\(n\)个点\(m\)条边的无向连通带权图, 要求删边至最多剩余\(k\)条边。定义“好点”是指删边后,\(1\)号节点到它的最短路长度仍然等于原图最短路长度的节点。要求满足给定条件下,删边后“好点”个数最多,求一个满足要求的图。

    思路:先求出最短路径树。然后从根开始做DFS,直到剩余k条边为止。DFS到的边即为需要剩余的边。

  • 相关阅读:
    Python修饰器 ,控制授权,通过ini配置文件 使用密钥 给函数限制试用期和过期后试用次数
    excel vba 自定义函数 使用正则表达式提取字符串
    python 值比较判断,np.nan is np.nan 却 np.nan != np.nan ,pandas 单个数据框/单元格 值判断nan
    python 读取中文CSV 'gbk' codec can't decode bytes in position 2-3:illegal multibyte sequence
    python ipython [Errno 22] invalid mode ('rb') or filename 、IDE工作路径
    windows下 python 添加PYTHONPATH 环境变量
    pandas(python2) 读取中文数据,处理中文列名
    qq邮箱 微信提醒不通知
    python :import error
    python推荐淘宝物美价廉商品 2.0
  • 原文地址:https://www.cnblogs.com/miraclepbc/p/16321867.html
Copyright © 2020-2023  润新知