1. 程式人生 > 其它 >單源最短路的綜合應用(複習整理)

單源最短路的綜合應用(複習整理)

目錄

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;
}