「筆記」Johnson 全源最短路與 Primal-Dual 原始對偶演算法

Luckyblock發表於2024-04-28

目錄
  • 寫在前面
  • Johnson 全源最短路
  • Primal-Dual 原始對偶演算法
  • 寫在最後

寫在前面

媽的我的網路流板子常數怎麼這麼 tama 的大

上網找牛逼板子的時候看到了 Primal-Dual 原始對偶演算法覺得很牛逼,抄!

然後發現這東西和 Johnson 思想基本一致,於是順帶著把 Johnson 也學一下。

Johnson 全源最短路

StudyingFather 寫太好了,感覺我都沒必要寫了。扔一份程式碼在這裡算了。

模板題:P5905 【模板】全源最短路(Johnson)

//
/*
By:Luckyblock

https://www.luogu.com.cn/problem/P5905
*/
#include <bits/stdc++.h>
#define LL long long
#define pli std::pair<LL,int>
#define mp std::make_pair
const int kN = 3e3 + 10;
const int kM = 1e5 + 10;
const int kInf = 1e9;
//=============================================================
int n, m;
int edgenum, head[kN], v[kM], ne[kM];
int cnt[kN];
bool vis[kN];
LL w[kM], h[kN], dis[kN];
//=============================================================
void Add(int u_, int v_, int w_) {
  v[++ edgenum] = v_;
  w[edgenum] = w_;
  ne[edgenum] = head[u_];
  head[u_] = edgenum;
}
bool Spfa(int s_) {
  std::queue <int> q;
  for (int i = 0; i <= n; ++ i) {
    cnt[i] = vis[i] = 0;
    h[i] = kInf;
  }
  q.push(s_);
  h[s_] = 0;
  vis[s_] = true;

  while (!q.empty()) {
    int u_ = q.front(); q.pop();
    vis[u_] = false;
    for (int i = head[u_]; i; i = ne[i]) {
      int v_ = v[i], w_ = w[i];
      if (h[u_] + w_ < h[v_]) {
        h[v_] = h[u_] + w_;
        cnt[v_] = cnt[u_] + 1;
        if (cnt[v_] > n) return false;
        if (!vis[v_]) q.push(v_), vis[v_] = true;
      }
    }
  }
  return true;
}
void Dijkstra(int s_) {
  std::priority_queue<pli> q;
  for (int i = 1; i <= n; ++ i) {
    vis[i] = 0, dis[i] = kInf;
  }
  dis[s_] = 0;
  q.push(mp(0, s_));
  while (!q.empty()) {
    int u_ = q.top().second; q.pop();
    if (vis[u_]) continue;
    vis[u_] = true;
    for (int i = head[u_]; i; i = ne[i]) {
      int v_ = v[i], w_ = w[i];
      if (dis[u_] + w_ < dis[v_]) {
        dis[v_] = dis[u_] + w_;
        q.push(mp(-dis[v_], v_));
      }
    }
  }
}
//=============================================================
int main() {
  //freopen("1.txt", "r", stdin);
  std::ios::sync_with_stdio(0), std::cin.tie(0);
  std::cin >> n >> m;
  for (int i = 1; i <= m; ++ i) {
    int u_, v_, w_; std::cin >> u_ >> v_ >> w_;
    Add(u_, v_, w_);
  }
  for (int i = 1; i <= n; ++ i) Add(0, i, 0);
  if (!Spfa(0)) {
    std::cout << -1 << "\n";
    return 0;
  }
  for (int u = 1; u <= n; ++ u) {
    for (int i = head[u]; i; i = ne[i]) {
      w[i] += h[u] - h[v[i]];
    }
  }
  for (int i = 1; i <= n; ++ i) {
    Dijkstra(i);
    LL ans = 0;
    for (int j = 1; j <= n; ++ j) {
      if (dis[j] == kInf) ans += 1ll * j * kInf;
      else ans += 1ll * j * (dis[j] + h[j] - h[i]);
    }
    std::cout << ans << "\n";
  }
  return 0;
}

Primal-Dual 原始對偶演算法

模板題:P3381 【模板】最小費用最大流

//
/*
By:Luckyblock

https://www.luogu.com.cn/problem/P3381
*/
#include <bits/stdc++.h>
#define LL long long
#define pli std::pair<LL,int>
#define mp std::make_pair
const int kN = 5e3 + 10;
const int kM = 2e5 + 10;
const LL kInf = 1e18 + 2077;
//=============================================================
int n, m, S, T;
int edgenum = 1, head[kN], v[kM], ne[kM];
bool vis[kN];
LL w[kM], c[kM], h[kN], dis[kN];
LL maxf, minc;
struct Previous_Node {
  int node, edge;
} from[kN];
//=============================================================
void Add(int u_, int v_, LL w_, LL c_) {
  v[++ edgenum] = v_; 
  w[edgenum] = w_;
  c[edgenum] = c_;
  ne[edgenum] = head[u_];
  head[u_] = edgenum;
}
void Spfa(int s_) {
  std::queue <int> q;
  for (int i = 0; i <= n; ++ i) {
    vis[i] = 0;
    h[i] = kInf;
  }
  q.push(s_);
  h[s_] = 0;
  vis[s_] = true;

  while (!q.empty()) {
    int u_ = q.front(); q.pop();
    vis[u_] = false;
    for (int i = head[u_]; i; i = ne[i]) {
      int v_ = v[i], w_ = w[i], c_ = c[i];
      if (w_ && h[u_] + c_ < h[v_]) {
        h[v_] = h[u_] + c_;
        if (!vis[v_]) q.push(v_), vis[v_] = true;
      }
    }
  }
}
bool Dijkstra(int s_) {
  std::priority_queue<pli> q;
  for (int i = 1; i <= n; ++ i) {
    vis[i] = 0, dis[i] = kInf;
  }
  dis[s_] = 0;
  q.push(mp(0, s_));
  while (!q.empty()) {
    int u_ = q.top().second; q.pop();
    if (vis[u_]) continue;
    vis[u_] = true;
    for (int i = head[u_]; i; i = ne[i]) {
      int v_ = v[i], w_ = w[i], nc_ = c[i] + h[u_] - h[v_];
      if (w_ && dis[u_] + nc_ < dis[v_]) {
        dis[v_] = dis[u_] + nc_;
        from[v_] = (Previous_Node) {u_, i};
        if (!vis[v_]) q.push(mp(-dis[v_], v_));
      }
    }
  }
  return dis[T] != kInf;
}
void MCMF() {
  Spfa(S);
  while (Dijkstra(S)) {
    LL minf = kInf;
    for (int i = 1; i <= n; ++ i) h[i] += dis[i];
    for (int i = T; i != S; i = from[i].node) minf = std::min(minf, w[from[i].edge]);
    for (int i = T; i != S; i = from[i].node) {
      w[from[i].edge] -= minf;
      w[from[i].edge ^ 1] += minf;
    }
    maxf += minf;
    minc += minf * h[T];
  }
  return ;
}
//=============================================================
int main() {
  // freopen("1.txt", "r", stdin);
  std::ios::sync_with_stdio(0), std::cin.tie(0);
  std::cin >> n >> m >> S >> T;
  for (int i = 1; i <= m; ++ i) {
    int u_, v_, w_, c_; std::cin >> u_ >> v_ >> w_ >> c_;
    Add(u_, v_, w_, c_);
    Add(v_, u_, 0, -c_);
  }
  MCMF();
  std::cout << maxf << " " << minc;
  return 0;
}

寫在最後

參考:

  • [洛穀日報#242]Johnson 全源最短路徑演算法學習筆記 - StudyingFather
  • 費用流 - OI Wiki

相關文章