单源最短路的综合应用(复习整理)
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\),\(r\)的最大值为\(1e6\),但是让\(r>1e6\),当最终二分的结果大于\(1e6\)时,代表不存在路径
#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;
}