前言
如果還沒有學過最大流,戳這裡。
本文用到了優化的 \(SPFA\) , 簡易的程式碼。
一組簡單的小樣例:
輸入
4 5 4 3
4 2 30 2
4 3 20 3
2 3 20 1
2 1 30 9
1 3 40 5
輸出
50 280
思路
對於滿足最大流合法的流函式可能有很多,但加上邊的單位花費,則方案會減少很多。
實質上是在 \(Edmond-Karp\) 演算法上進行改進。
\(Edmond-Karp\) 演算法是對於殘量網路上僅找到 \(1\) 條增廣路進行增廣。
而求最小費用最大流只需要在尋找增廣路的過程中尋找花費最小的增廣路。
建圖
需要包含四個資訊:終點節點編號,邊的容量,邊的花費,相反的邊的編號。
struct Node {
int to, val, cost, rev;
Node() {}
Node(int T, int L, int C, int R) {
to = T;//終點節點編號
val = L;//邊的容量
cost = C;//邊的花費
rev = R;//相反的邊的編號
}
};
確定增廣路徑
由 \(SPFA\) 實現,若不存在增廣路徑,則停止增廣。
增廣
在 \(SPFA\) 的過程中記錄這條最短路路徑編號,並記錄這條路上的最小剩餘容量。
用一個 \(while\) 迴圈尋找這條路徑,並且直接記錄這條路徑的花費即可。
C++程式碼
#include <map>
#include <queue>
#include <cstdio>
#include <vector>
#include <cstring>
using namespace std;
#define INF 0x3f3f3f3f
#define Min(a, b) ((a) < (b) ? (a) : (b))
void Quick_Read(int &N) {
N = 0;
int op = 1;
char c = getchar();
while(c < '0' || c > '9') {
if(c == '-')
op = -1;
c = getchar();
}
while(c >= '0' && c <= '9') {
N = (N << 1) + (N << 3) + (c ^ 48);
c = getchar();
}
N *= op;
}
const int MAXN = 5e3 + 5;
struct Node {
int to, val, cost, rev;
Node() {}
Node(int T, int L, int C, int R) {
to = T;
val = L;
cost = C;
rev = R;
}
};
vector<Node> v[MAXN];
pair<int, int> pre[MAXN];
deque<int> q;
int dis[MAXN], maf[MAXN];
bool inque[MAXN], vis[MAXN];
int n, m, s, t;
int ans;
bool SPFA() {
for(int i = 1; i <= n; i++)
inque[i] = false, dis[i] = INF, maf[i] = -INF;
int iqn = 1, fis = 0;//優化
maf[s] = INF;
dis[s] = 0;
inque[s] = true;
q.push_back(s);
while(!q.empty()) {
int now = q.front(); q.pop_front();
inque[now] = false;
fis -= dis[now];
iqn--;
int SIZ= v[now].size();
for(int i = 0; i < SIZ; i++) {
int next = v[now][i].to;
if(dis[next] > dis[now] + v[now][i].cost && v[now][i].val) {
maf[next] = Min(maf[now], v[now][i].val);//記錄最小剩餘容量
pre[next].first = now;//記錄路徑
pre[next].second = i;
dis[next] = dis[now] + v[now][i].cost;
if(!inque[next]) {
inque[next] = true;
if(q.empty() || dis[next] > dis[q.front()] || dis[next] * iqn <= fis)
q.push_back(next);
else
q.push_front(next);
fis += dis[next] + v[now][i].cost;
iqn++;
}
}
}
}
return dis[t] != INF;//是否存在增廣路
}
int Update() {
int now = t;
while(now != s) {
int next = pre[now].first;
int i = pre[now].second;
v[next][i].val -= maf[t];//減去最小該路徑的最小剩餘容量,更新剩餘流量
v[now][v[next][i].rev].val += maf[t];//流量守恆
ans += v[next][i].cost * maf[t];//記錄價值
now = next;
}
return maf[t];
}
int Edmond_Karp() {//求解最小費用最大流
int res = 0;
while(SPFA()) {
do {
res += Update();
} while(vis[t]);
}
return res;
}
void Read() {
int A, B, C, D;
Quick_Read(n);
Quick_Read(m);
Quick_Read(s);
Quick_Read(t);
for(int i = 1; i <= m; i++) {
Quick_Read(A);
Quick_Read(B);
Quick_Read(C);
Quick_Read(D);
int idA = v[A].size();
int idB = v[B].size();
v[A].push_back(Node(B, C, D, idB));
v[B].push_back(Node(A, 0, -D, idA));//記得雙向存邊
}
}
int main() {
Read();
printf("%d ", Edmond_Karp());
printf("%d", ans);
return 0;
}