單源最短路的綜合應用(複習整理)
阿新 • • 發佈:2022-02-23
目錄
,\(r\)的最大值為\(1e6\),但是讓\(r>1e6\),當最終二分的結果大於\(1e6\)時,代表不存在路徑
1135. 新年好 - AcWing題庫
分別求出{起點,a,b,c,d,e}這6個點到其他點的最短路,然後列舉後5個點所有的排列情況,然後求出每種情況的距離,取最小值
#include <iostream> #include <algorithm> #include <cstring> #include <queue> using namespace std; typedef pair<int, int> PII; constexpr int M = 2e5 + 100, N = 2e5 + 100, INF = 0x3f3f3f3f; int h[N], e[M], ne[M], w[M], idx; int dist[6][N], n, m, q[6], res = INF; bool st[N]; inline void add(int a, int b, int c) { e[++idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx; } inline void dijkstra(int s, int dist[]) { for (int i = 0; i <= n; i++) { dist[i] = INF, st[i] = 0; } dist[s] = 0; priority_queue<PII, vector<PII>, greater<PII>> heap; heap.push({0, s}); while (heap.size()) { auto t = heap.top(); heap.pop(); int ver = t.second; if (st[ver]) continue; st[ver] = 1; for (int i = h[ver]; i; i = ne[i]) { int j = e[i], d = t.first + w[i]; if (dist[j] > d) { dist[j] = d; heap.push({dist[j], j}); } } } } void dfs(int u) { static int p[6]; static bool vis[6]; if (u >= 5) { int s = 0; int t = 0; for (int i = 0; i < u; i++) { int d = dist[t][q[p[i]]]; if (d >= INF) return; s += d; t = p[i]; } res = min(res, s); return; } for (int i = 1; i <= 5; i++) { if (vis[i]) continue; vis[i] = 1; p[u] = i; dfs(u + 1); vis[i] = 0; } } int main() { scanf("%d%d", &n, &m); q[0] = 1; for (int i = 1; i <= 5; i++) scanf("%d", q + i); while (m --) { int a, b, c; scanf("%d%d%d", &a, &b, &c); add(a, b, c), add(b, a, c); } for (int i = 0; i <= 5; i++) dijkstra(q[i], dist[i]); dfs(0); printf("%d\n", res); return 0; }
340. 通訊線路 - AcWing題庫
本題讓在一條\(1\to n\)的路徑中,刪去\(k\) 條邊後,剩餘最大邊作為花費,讓你求最小花費
我個人首先能想到的做法,是選取最短路後,選取第\(k+1\)大的邊作為答案,顯然這個是不行的
但是進一步可以發現:花費\(x\)越大,選取的電纜越少;\(x\)越小,選取的電纜越多.這樣一來我們就可以通過二分來解決這個問題
那麼如何確定從1到n中需要選擇多少個電纜?當從1出發後,如果遇到的邊w
大於x
,那麼就代表需要選擇一個電纜.這樣一來,從1到n最少需要選擇多少個電纜,可以看作從1出發、終點是n,邊權是w>x
的最短路問題
特別地,當不存在1到n的路徑時,即選擇的電纜數為0,就會不斷收縮右區間\(r\)
#include <iostream> #include <algorithm> #include <cstring> #include <deque> using namespace std; typedef pair<int, int> PII; constexpr int N = 1e5 + 100, INF = 0x3f3f3f3f; int h[N], e[N], ne[N], w[N], idx; bool st[N]; int n, p, k, dist[N]; inline void add(int a , int b, int c) { e[++idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx; } inline bool check(int x) { for (int i = 0; i <= n; i++) { dist[i] = INF, st[i] = 0; } dist[1] = 0; deque<int> q; q.emplace_back(1); while (q.size()) { int t=q.front(); q.pop_front(); if (st[t]) continue; st[t] = 1; for (int i = h[t]; i; i = ne[i]) { int j = e[i], d = w[i] > x; if (dist[j] > dist[t] + d) { dist[j] = dist[t] + d; if (d) q.emplace_back(j); else q.emplace_front(j); } } } return dist[n] <= k; } int main() { scanf("%d%d%d", &n, &p, &k); while (p --) { int a, b, c; scanf("%d%d%d", &a, &b, &c); add(a, b, c); add(b, a, c); } int l = 0, r = 1e6 + 100; while (l < r) { int mid = l + r >> 1; if (check(mid)) r = mid; else l = mid + 1; } if (r > (int)1e6) puts("-1"); else printf("%d\n", r); return 0; }
342. 道路與航線 - AcWing題庫
341. 最優貿易 - AcWing題庫
本題讓你在1~n的路徑上買賣一次物品,問你最終能收穫的最大值是多少
我們可以通過dp的方式,根據點來劃分不同的狀態:令f[i]
表示經過點i時,購買需要的最少的花費;g[i]
表示經過點i時,賣出獲得的最大的收益
由於先買後賣的原則,求從1出發到各個點的最小花費,收益應該是求多個點出發到n的最大收益,所以我們可以反向建邊,在反向路徑上求從n出發到各個點的最大收益。
由於本題存在環,並不能直接用dp來迭代,所以要用spfa演算法進行轉移,轉移關係為:
-
f[i] = min(f[j], w[i])
-
g[i] = max(g[j], w[i])
最後列舉所有點的收益情況,取最大值即可
#include <iostream>
#include <algorithm>
#include <cstring>
#include <queue>
using namespace std;
constexpr int N = 1e5 + 100, M = 1e6 + 100;
int h[N], hs[N], e[M], ne[M], w[N], idx;
int f[N], g[N], n, m;
inline void add(int h[], int a, int b) {
e[++idx] = b, ne[idx] = h[a], h[a] = idx;
}
inline void spfa(int s) {
bool st[N] = {0};
int *H, *dist;
if (s == 1) {
H = h;
dist = f;
memset(f, 0x3f, sizeof f);
} else {
H = hs;
dist = g;
memset(g, -0x3f, sizeof g);
}
queue<int> q;
dist[s] = w[s];
q.emplace(s);
while (q.size()) {
int t = q.front(); q.pop();
st[t] = 0;
for (int i = H[t]; i; i = ne[i]) {
int j = e[i];
bool ok = 0;
if (s == 1) {
int d = min(dist[t], w[j]);
if (dist[j] > d) dist[j] = d, ok = 1;
} else if (s == n) {
int d = max(dist[t], w[j]);
if (dist[j] < d) dist[j] = d, ok = 1;
}
if (ok && !st[j]) {
st[j] = 1;
q.emplace(j);
}
}
}
}
int main() {
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; i++) scanf("%d", w + i);
while (m --) {
int t, a, b;
scanf("%d%d%d", &a, &b, &t);
add(h, a, b), add(hs, b, a);
if (t == 2) add(h, b, a), add(hs, a, b);
}
spfa(1);
spfa(n);
int res = 0;
for (int i = 1; i <= n; i++) {
res = max(res, g[i] - f[i]);
}
printf("%d\n", res);
return 0;
}