您的位置:首页 > 其它

战争和物流(uvalive 4080)

2018-02-06 14:35 459 查看
题目链接 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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: