如何用求次長邊——POJ 3255 題解
阿新 • • 發佈:2018-10-26
problem 問題 while 說明 這樣的 pty 理由 數組 等於 為 \(u\) 到 \(v\) 的長度)。 然後我們判斷一下假如 \(D\) 的值不等於從 \(1\) 到 \(n\) 的最短路長度,就用這個 \(D\) 去更新答案。
到達 \(n\) 的最短路的長度就是 \(dis1[u] + w(u,v) + dis2[v]\),然後我們用這個東西去更新答案,那麽得到的一定至少一條邊不在最短路上的最短路,因為一個圖中除了最短路,其余的路都不比最短路長,所以我們這樣找到的一定是除了最短路以外的最短的邊,即次短路。
題目大意
給你一個 \(n\) 個點,\(m\) 條邊的無向圖,求出這個無向圖中從1到n的次短路。其中\(n \le 5000\),\(m \le 100000\)。
題目傳送門
POJ 3255
思路
其實求次長路是很簡單的一個問題,但是網上卻有很多算法都過於復雜了。首先我們先求出從1到每個結點的最短路長度(用Dijkstra或者是SPFA都可以),存入數組 \(dis1\) 中,然後再求出從結點 \(n\) 到任意結點的最短路的長度,存入數組 \(dis2\) 中。
然後我們枚舉這個圖中的所有邊 \((u, v)\) 然後我們設 \(D = dis1[u] + w(u,v) + dis2[v]\)(\(w(u,v)\)
理由
因為次短路中一定至少有一條邊與最短路中不一樣,所以我們可以枚舉一條邊,強制從 \(1\) 到 \(n\) 的時候一定要經過這條邊,然後再用這樣的最短路去更新答案。有的時候枚舉的這條邊恰好在最短路上(最短路不只一條),那麽我們如何判斷呢?假如枚舉到邊 \((u,v)\) 那麽假如 \(dis1[u] + w(u,v) + dis2[v] = dis1[n]\),就說明這條邊在最短路上。然後假如這個邊不在最短路上,那麽一定要通過這個邊從 \(1\)
代碼
#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 題解