如何用求次长边——POJ 3255 题解
Posted lixiao189
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了如何用求次长边——POJ 3255 题解相关的知识,希望对你有一定的参考价值。
题目大意
给你一个 (n) 个点,(m) 条边的无向图,求出这个无向图中从1到n的次短路。其中(n le 5000),(m le 100000)。
题目传送门
思路
其实求次长路是很简单的一个问题,但是网上却有很多算法都过于复杂了。首先我们先求出从1到每个结点的最短路长度(用Dijkstra或者是SPFA都可以),存入数组 (dis1) 中,然后再求出从结点 (n) 到任意结点的最短路的长度,存入数组 (dis2) 中。
然后我们枚举这个图中的所有边 ((u, v)) 然后我们设 (D = dis1[u] + w(u,v) + dis2[v])((w(u,v))为 (u) 到 (v) 的长度)。 然后我们判断一下假如 (D) 的值不等于从 (1) 到 (n) 的最短路长度,就用这个 (D) 去更新答案。
理由
因为次短路中一定至少有一条边与最短路中不一样,所以我们可以枚举一条边,强制从 (1) 到 (n) 的时候一定要经过这条边,然后再用这样的最短路去更新答案。有的时候枚举的这条边恰好在最短路上(最短路不只一条),那么我们如何判断呢?假如枚举到边 ((u,v)) 那么假如 (dis1[u] + w(u,v) + dis2[v] = dis1[n]),就说明这条边在最短路上。然后假如这个边不在最短路上,那么一定要通过这个边从 (1) 到达 (n) 的最短路的长度就是 (dis1[u] + w(u,v) + dis2[v]),然后我们用这个东西去更新答案,那么得到的一定至少一条边不在最短路上的最短路,因为一个图中除了最短路,其余的路都不比最短路长,所以我们这样找到的一定是除了最短路以外的最短的边,即次短路。
代码
#include <iostream>
#include <cstdio>
#include <queue>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 5e3 + 5;
const int M = 2e5 + 5;
int n, m;
int u, v, w;
int cnt = 0;
int head[N];
int dis1[N];
int dis2[N];
struct EDGE {
int s;
int e;
int w;
int nxt;
} Edge[M];
void add(int u, int v, int w) {
++cnt;
Edge[cnt].s = u;
Edge[cnt].e = v;
Edge[cnt].w = w;
Edge[cnt].nxt = head[u];
head[u] = cnt;
}
int read() {
int s = 0, w = 1;
char ch = getchar();
while (ch < '0' || ch > '9') {
if (ch == '-') w = -1;
ch = getchar();
}
while (ch >= '0' && ch <='9') {
s = s * 10 + ch - '0';
ch = getchar();
}
return s * w;
}
void write(int x) {
if (x < 0) putchar('-'), x = -x;
if (x > 9) write(x / 10);
putchar(x % 10 + '0');
}
void Spfa1(int x) {
queue<int> q;
q.push(x);
memset(dis1, 0x3f, sizeof(dis1)); //初始化dis1
dis1[x] = 0;
while (!q.empty()) {
int now = q.front(); q.pop();
for (register int i = head[now]; i; i = Edge[i].nxt) {
if (dis1[Edge[i].e] > dis1[now] + Edge[i].w) {
dis1[Edge[i].e] = dis1[now] + Edge[i].w;
q.push(Edge[i].e);
}
}
}
}
void Spfa2(int x) {
queue<int> q;
q.push(x);
memset(dis2, 0x3f, sizeof(dis2)); //初始化dis1
dis2[x] = 0;
while (!q.empty()) {
int now = q.front(); q.pop();
for (register int i = head[now]; i; i = Edge[i].nxt) {
if (dis2[Edge[i].e] > dis2[now] + Edge[i].w) {
dis2[Edge[i].e] = dis2[now] + Edge[i].w;
q.push(Edge[i].e);
}
}
}
}
int main(int argc, char const *argv[]) {
n = read(), m = read();
for (register int i = 1; i <= m; ++i) {
u = read(), v = read(), w = read();
add(u, v, w);
add(v, u, w); //添加双向边
}
Spfa1(1);
Spfa2(n);
int ans = 0x3f3f3f3f;
for (register int i = 1; i <= cnt; ++i) {
int dis_tmp = dis1[Edge[i].s] + Edge[i].w + dis2[Edge[i].e];
if (dis_tmp != dis1[n] && dis_tmp < ans) ans = dis_tmp;
}
write(ans);
return 0;
}
以上是关于如何用求次长边——POJ 3255 题解的主要内容,如果未能解决你的问题,请参考以下文章