目錄
- 寫在前面
- 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