Loading

单源最短路的综合应用(复习整理)

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;
}
posted @ 2022-02-23 22:57  Frank_Ou  阅读(22)  评论(0编辑  收藏  举报