网络流模板
EK求最大可行流
#include <iostream>
#include <queue>
#include <cstring>
using namespace std;
const int N = 2000, M = 20090;
int h[N], ne[M], to[M], f[M], idx;
int s, t;
int pre[N], d[N];
queue<int>q;
void add(int u, int v, int w) {
ne[idx] = h[u], to[idx] = v, f[idx] = w, h[u] = idx++;
ne[idx] = h[v], to[idx] = u, f[idx] = 0, h[v] = idx++;
}
bool vis[N];
bool bfs() {
while (!q.empty())q.pop();
q.push(s);
memset(vis, 0, sizeof vis);
vis[s] = 1;
d[s] = 99999999;//d数组是指可行流。
while (!q.empty()) {
auto u = q.front();
q.pop();
for (int i = h[u]; ~i; i = ne[i]) {
int v = to[i];
if (!vis[v] && f[i] > 0) {
vis[v] = 1;
q.push(v);
pre[v] = i;//记录路径,这样找到了增广路,简单可回溯。
d[v] = min(f[i], d[u]);//因为是可行流,所以每次要取最小。
if (v == t)return 1;
}
}
}
return 0;
}
int EK() {
int ret = 0;
while (bfs()) {//如果能够找到增广路,那么把那条增广路上的可行流加入答案,然后
int T = t;
ret += d[t];
while (T != s) {//更新这条增广路上的所有流量,即更新了残留网络。
int now = pre[T];//now是指增广路中指向T点的边的编号
f[now] -= d[t];
f[now^1]+=d[t];
T = to[now^1]; //now^1是指在增广路上从T指出的编号。
}
}
return ret;
}
int main() {
int n, m;
scanf("%d%d%d%d", &n, &m, &s, &t);
memset(h, -1, sizeof h);idx = 0;
while (m--) {
int u, v, w;scanf("%d%d%d", &u, &v, &w);
add(u, v, w);//正常建立边,然后构造残留网络,显然。
}
printf("%d
", EK());
}
dinic求最大可行流
#include <iostream>
#include <queue>
#include <cstring>
using namespace std;
const int N = 10009, M = 200010;
const int inf = 99999999;
int n, m, S, T;
int h[N], ne[M], to[M], f[M], idx;
void add(int u, int v, int w) {
ne[idx] = h[u], to[idx] = v, f[idx] = w, h[u] = idx ++;
ne[idx] = h[v], to[idx] = u, f[idx] = 0, h[v] = idx ++;
}
queue<int>q;
int d[N];
int cur[N];
bool bfs() {
while (!q.empty()) q.pop();
q.push(S);
memset(d, -1, sizeof d);//d数组是深度,防止绕进环中去。
d[S] = 0;
cur[S] = h[S];//必然第一个弧就是,但是容易忘。
while (!q.empty()) {
auto u = q.front();q.pop();
for (int i = h[u]; ~i; i = ne[i]) {
int v = to[i];
if (f[i] && d[v] == -1) {//找增广路,所以必须f[i] > 0
d[v] = 1 + d[u];
cur[v] = h[v];//只会遍历一次。
if (v == T)return 1;
q.push(v);
}
}
}
return 0;
}
int dfs(int u, int limit) {
if (u == T)return limit;//容易忘。终点直接返回
int flow = 0;//点流向后面的。
for (int i = cur[u]; ~i && flow < limit; i = ne[i]) {//从当前弧开始,如果之前
int v = to[i];
cur[u] = i;//回溯到u点之前,然后又到了u点,没必要从头开始了。
if (d[v] == d[u] + 1 && f[i]) {//d是按照层数走,f必须大于0。
int t = dfs(v, min(f[i], limit - flow));//t是走向出去的流量。
if (t <= 0) d[v] = -1;//一滴也没有了,点就可以删掉了。
f[i] -= t;
f[i^1] += t;//更新残留网络。
flow += t;//当前总的流向后面的加上当前v流向后面的。
}
}
return flow;
}
int dinic() {
int ret = 0, flow;
while (bfs()) {
while (flow = dfs(S, inf))ret +=flow;
}
return ret;
}
int main() {
memset(h, -1, sizeof h);
scanf("%d%d%d%d", &n, &m, &S, &T);
for (int i = 1; i <= m; i ++) {
int u, v, w;
scanf("%d%d%d",&u,&v,&w);
add(u, v, w);
}
printf("%d
", dinic());
}
struct DI {
ll n, m, S, T;
ll h[N], ne[M], to[M], f[M], idx;
void add(ll u, ll v, ll w) {
ne[idx] = h[u], to[idx] = v, f[idx] = w, h[u] = idx++;
ne[idx] = h[v], to[idx] = u, f[idx] = 0, h[v] = idx++;
}
queue<ll> q;
ll d[N];
ll cur[N];
void init() {
memset(h, -1, sizeof h);
idx = 0;
}
bool bfs() {
while (!q.empty()) q.pop();
q.push(S);
memset(d, -1, sizeof d); // d数组是深度,防止绕进环中去。
d[S] = 0;
cur[S] = h[S]; //必然第一个弧就是,但是容易忘。
while (!q.empty()) {
auto u = q.front();
q.pop();
for (ll i = h[u]; ~i; i = ne[i]) {
ll v = to[i];
if (f[i] && d[v] == -1) { //找增广路,所以必须f[i] > 0
d[v] = 1 + d[u];
cur[v] = h[v]; //只会遍历一次。
if (v == T) return 1;
q.push(v);
}
}
}
return 0;
}
ll dfs(ll u, ll limit) {
if (u == T) return limit; //容易忘。终点直接返回
ll flow = 0; //点流向后面的。
for (ll i = cur[u]; ~i && flow < limit;
i = ne[i]) { //从当前弧开始,如果之前
ll v = to[i];
cur[u] = i; //回溯到u点之前,然后又到了u点,没必要从头开始了。
if (d[v] == d[u] + 1 && f[i]) { // d是按照层数走,f必须大于0。
ll t = dfs(v, min(f[i], limit - flow)); // t是走向出去的流量。
if (t <= 0) d[v] = -1; //一滴也没有了,点就可以删掉了。
f[i] -= t;
f[i ^ 1] += t; //更新残留网络。
flow += t; //当前总的流向后面的加上当前v流向后面的。
}
}
return flow;
}
ll dinic() {
ll ret = 0, flow;
while (bfs()) {
while (flow = dfs(S, inf)) ret += flow;
}
// init();
return ret;
}
void makemap() {
init();
scanf("%d", &n);
S = N-2;
T = N - 1;
for (int i = 1; i <= n * 2; i++) add(S, i, 1);
for (int i = 1; i <= 2 * n; i++) {
int u, v;
scanf("%d%d", &u, &v);
u += 3 * n;
v += 3 * n;
add(i, u, 1);
add(i, v, 1);
}
for (int i = 1; i <= n; i++) {
add(i + 3 * n, T, 2);
}
}
} dinic;
无源汇上下界可行流
首先可转化为有源汇上界最大流,即普通最大流。首先,某个点要满足两个限制,一个是流量守恒,另一个是容量守恒。
设 (l(u, v)) 为点 (u) 到点 (v) 的下界,然后设 (u(u, v)) 是点 (u) 到点 (v) 的上界。
显然 $$l(u, v) <= f(u, v) <= u(u, v) $$
如果突发奇想,全减掉 (l(u, v)),那么左边就是 (0),是不是就转化成了普通的流量网络?即
[0 <= f(u, v) - l(u, v) <= u(u, v) - l(u, v)
]
但是流量守恒却不一定。
假设点左边都是满流,那么初始进入点 (x) 的流量设为 (F_{in}),那么 (F_{in} = sum_{uin V}f(u, x)),进行剪掉 (l(u, x)),后,设 (F_{in, sub} = sum_{uin V}l(u, x))。
假设点左边都是满流,那么初始从点 (x) 流出去的流量设为 (F_{out}),那么 (F_{in} = sum_{vin V}f(x, v)),进行剪掉 (l(x, v)),后,设 (F_{in, sub} = sum_{vin V}l(x, v))。
所以当 (F_{in, sub}) 多了,即左边减得太多了,就应该是从 (S) 加一条边到 (x),然后容量限制为 (F_{in, sub} - F_{out, sub}),如果右边剪得太多了,那么就应该从 (x) 加一条边到 (T),然后容量限制为 (F_{out,sub} - F_{in, sub})。
然后代码中 (A) 数组代表的意义是 (F_{in, sub} - F_{out, sub})。
#include <iostream>
#include <cstring>
#include <queue>
using namespace std;
const int N = 1e5, M = 2e6;
const int inf = 0x3f3f3f3f;
int n, m, S, T;
int h[N], ne[M], l[M], to[M], f[M], idx;
int A[N];
void add(int u, int v, int w, int low) {
ne[idx] = h[u], to[idx] = v, f[idx] = w, l[idx] = low, h[u] = idx++;
ne[idx] = h[v], to[idx] = u, f[idx] = 0, l[idx] = low, h[v] = idx++;
}
queue<int>q;
int d[N], cur[N];
bool bfs() {
while (!q.empty())q.pop();
q.push(S);
memset(d, -1, sizeof d);
d[S] = 0;
cur[S] = h[S];
while (!q.empty()) {
auto u = q.front();q.pop();
for (int i = h[u]; ~i; i = ne[i]) {
int v = to[i];
if (f[i] && d[v] == -1) {
d[v] = d[u] + 1;
cur[v] = h[v];
q.push(v);
if (v == T)return 1;
}
}
}
return 0;
}
int dfs(int u, int limit) {
if (u == T)return limit;
int flow = 0;
for (int i = cur[u]; ~i && flow < limit; i = ne[i]) {
int v = to[i];
cur[u] = i;
if (d[v] == d[u] + 1 && f[i]) {
int t = dfs(v, min(f[i], limit - flow));
if (t <=0)d[v] = -1;
flow += t, f[i] -= t, f[i ^ 1] += t;
}
}
return flow;
}
int dinic() {
int ret = 0, flow;
while (bfs())while (flow = dfs(S, inf))ret += flow;
return ret;
}
int main() {
int sum = 0;
scanf("%d%d", &n, &m);
memset(h, -1, sizeof h);
for (int i = 1; i <= m; i ++) {
int u, v, low, up;
scanf("%d%d%d%d", &u, &v, &low, &up);
int w = up - low;
A[u] -= low;
A[v] += low;
add(u, v, w, low);
}
S = 0, T = n + 1;
for (int i = 1; i <= n; i ++) {
if (A[i] > 0) {
add(S, i, A[i], 0);
sum += A[i];
} else if (A[i] < 0) {
add(i, T, -A[i], 0);
}
}
int max_flow = dinic();
if (max_flow == sum) {
puts("YES");
for (int i = 0; i < m * 2; i += 2) {
printf("%d
", f[i ^ 1] + l[i]);
}
return 0;
}
puts("NO");
}
最小费用最大流
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef long double ld;
const ll N = 1e6 + 9;
const ll inf = 0x3f3f3f3f;
const int M = 2 * N;
struct costflow {
int n, m, S, T;
int h[N], to[M], f[M], w[M], ne[M], idx;
int q[N], d[N], pre[N], incf[N];
bool st[N];
void add(int u, int v, int flow, int co) {
int a = u, b = v, c = flow, d = co;
to[idx] = b, f[idx] = c, w[idx] = d, ne[idx] = h[a], h[a] = idx++;
to[idx] = a, f[idx] = 0, w[idx] = -d, ne[idx] = h[b], h[b] = idx++;
}
bool spfa() {
int hh = 0, tt = 1;
memset(d, 0x3f, sizeof d);
memset(incf, 0, sizeof incf);
q[0] = S, d[S] = 0, incf[S] = inf;
while (hh !=tt) {
int t = q[hh++];
//cout << "?";
if (hh == N)hh =0;
st[t] = false;
for (int i = h[t]; ~i; i = ne[i]) {
int ver = to[i];
if (f[i] && d[ver] > d[t] + w[i]) {
d[ver] = d[t] + w[i];
pre[ver] = i;
incf[ver] = min(f[i], incf[t]);
if (!st[ver]) {
q[tt++] = ver;
if (tt == N) tt = 0;
st[ver] = 1;
}
}
}
}
return incf[T] > 0;
}
void EK(int &flow, int &Max_cost, int &Min_cost) {
flow = Min_cost = 0;
while (spfa()) {
int t = incf[T];
flow += t, Min_cost += t * d[T];
for (int i = T; i != S; i = to[pre[i]^1]) {
f[pre[i] ] -= t;
f[pre[i] ^ 1] += t;
}
}
for (int i = 0; i <= idx; i += 2) {
f[i] += f[i ^ 1];
f[i^1] = 0;
w[i] = -w[i];
w[i ^ 1] = -w[i ^ 1];
}
//cout << Min_cost << "?";
flow = Max_cost = 0;
while (spfa()) {
int t = incf[T];
flow += t, Max_cost += t * d[T];
for (int i = T; i != S; i = to[pre[i] ^ 1]) {
f[pre[i]] -= t;
f[pre[i] ^ 1] += t;
}
}
Max_cost*=-1;
}
void init() {
memset(h, -1, sizeof h);
idx = 0;
}
int a[N];
void make_map() {
init();
scanf("%d%d", &n, &m);
S = N-2, T = N - 1;
for (int i = 1; i <= n; i++) {
scanf("%d", &a[i]);
add(S, i, a[i], 0);
}
for (int i = 1; i <= m; i++) {
scanf("%d", &a[i]);
add(i + n, T, a[i], 0);
}
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
int x;
scanf("%d", &x);
add(i, j + n, inf, x);
}
}
int flow, min_cost, max_cost;
// cout << "?";
EK(flow, max_cost, min_cost);
printf("%d
%d
", min_cost, max_cost);
}
}EK;
int a[N];
void solve() {
EK.make_map();
}
signed main() {
ll t = 1;//cin >> t;
while (t--) {
solve();
}
}