Uvalive 4080 - Warfare And Logistics (最短路树)

题目链接 https://vjudge.net/problem/UVALive-4080

【题意】
给定一个n节点m条边的无向图,定义c为每对顶点的最短路之和,要求删掉一条边重新求一个c值c’,求出c’最大值.

【思路】
大白书330页例题,如果用floyd算法计算c,每尝试删除一条边都要重新计算一次,时间复杂度为O(n*n*n*m),很难承受。如果用n次Dijkstra计算单源最短路,时间复杂度味O(n*m*m*logn)。虽然看上去比之前的好,但由于佛洛依德算法的常数很小,实际运行时间差不多。这时候,可以考虑最短路树。因为在源点确定的情况下,只要最短路树不被破坏,起点到所有点的距离都不会发生改变。也就是说,只有删除最短路树上的n-1条边,最短路树才需要重新计算。这样,对于每个源点,最多只需求n次最短路而不是m次,时间复杂度降为O(n*n*m*logn),可以承受。而且在有重边时删除一条边应当用第二短的边来代替。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int inf = 0x3f3f3f3f;
const int maxn = 105;
const int maxm = 1050;

int n, m, L;
int p[maxn];
ll ans[maxm << 1];

struct Edge {
    int from, to, dist;
    Edge(int f, int t, int d) :from(f), to(t), dist(d) {}
};

struct HeapNode {
    int d, u;
    HeapNode(int dd, int uu) :d(dd), u(uu) {}
    bool operator<(const HeapNode& rhs) const {
        return d > rhs.d;
    }
};

struct Dijkstra {
    int n, m;
    vector<Edge> edges;
    vector<int> g[maxn];
    bool done[maxn];
    int d[maxn];
    int p[maxn];
    int del = -1;//被删除的边的编号(通过del^1找相对称的边)

    void init(int n) {
        this->n = n;
        for (int i = 0; i < n; ++i) g[i].clear();
        edges.clear();
    }

    void add(int from, int to, int dist) {
        edges.push_back(Edge(from, to, dist));
        m = edges.size();
        g[from].push_back(m - 1);
    }

    ll dijkstra(int s) {
        priority_queue<HeapNode> que;
        for (int i = 0; i < n; ++i) d[i] = inf;
        d[s] = 0;
        memset(done, 0, sizeof(done));
        que.push(HeapNode(0, s));
        while (!que.empty()) {
            HeapNode x = que.top();
            que.pop();
            int u = x.u;
            if (done[u]) continue;
            done[u] = true;
            for (int i = 0; i < g[u].size(); ++i) {
                if (del != g[u][i] && (del ^ 1) != g[u][i]) {//不能使用被删除的边
                    Edge& e = edges[g[u][i]];
                    if (d[e.to] > d[u] + e.dist) {
                        d[e.to] = d[u] + e.dist;
                        p[e.to] = g[u][i];
                        que.push(HeapNode(d[e.to], e.to));
                    }
                }
            }
        }
        ll ans = 0;
        for (int i = 0; i < n; ++i) {
            if (i == s) continue;
            ans += d[i] == inf ? L : d[i];
        }
        return ans;
    }
}solver;

int main() {
    while (scanf("%d%d%d", &n, &m, &L) == 3) {
        solver.init(n);
        memset(ans, 0, sizeof(ans));
        for (int i = 0; i < m; ++i) {
            int u, v, c;
            scanf("%d%d%d", &u, &v, &c);
            --u, --v;
            solver.add(u, v, c);
            solver.add(v, u, c);
        }

        ll ans1 = 0, ans2 = 0;
        for (int s = 0; s < n; ++s) {//枚举所有点作为源点
            ll tmp = solver.dijkstra(s);
            memcpy(p, solver.p, sizeof(p));
            ans1 += tmp;
            for (int i = 0; i < 2 * m; ++i) ans[i] += tmp;

            for (int i = 0; i < n; ++i) {//把最短路树上的边依次删除,再求最短路
                if (i == s) continue;
                solver.del = p[i];
                ans[p[i]] = ans[p[i] ^ 1] = ans[p[i]] - tmp + solver.dijkstra(s);
                //无向图每条边添加了2次,p[i]^1其实就是找和p[i]边相同的另一条边
            }
            solver.del = -1;//每次算完记得重新把del置为-1(-1^1==-2,而边的下标是0到2*m-1,所以对求最短路无影响)
        }
        for (int i = 0; i < 2 * m; ++i) {
            ans2 = max(ans2, ans[i]);
        }
        printf("%lld %lld\n", ans1, ans2);
    }
    return 0;
}
posted @ 2018-02-06 14:35  不想吃WA的咸鱼  阅读(143)  评论(0编辑  收藏  举报