网络流进阶
先把三道例题扔在这里吧qvq
无源汇有上下界可行流
有源汇有上下界最大流
有源汇有上下界最小流
无源汇有上下界可行流
首先默认把下界跑满 但显然这样是不满足流量平衡的
所以建立超级源S,超级汇T
把所有“入不敷出”的点向T连一条大小为 出流-入流 的边
把S向所有入流大的点连一条大小为 入流-出流 的边
此时跑一遍网络流 如果与S相连的边都满流了 那么T也满流了
整个图即可行 否则不可行
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <queue>
using namespace std;
const int N = 205;
const int M = 1e6;
const int inf = 0x3f3f3f3f;
struct Edge{
int v, f, next;
}edge[M];
int head[N], cur[N], esize = -1;
inline void addedge(int x, int y, int z){
//printf("%d %d %d\n", x, y, z);
edge[++esize] = (Edge){y, z, head[x]};
head[x] = esize;
edge[++esize] = (Edge){x, 0, head[y]};
head[y] = esize;
}
int n, m, S, T, cnt, mf;
int low[M], upp[M], in[N], out[N];
int dis[N], fro;
queue<int> que;
bool bfs(){
memset(dis, 0, sizeof(dis));
que.push(S); dis[S] = 1;
while(!que.empty()){
fro = que.front(); que.pop();
for(int i = head[fro], vv; ~i; i = edge[i].next){
vv = edge[i].v;
if(!dis[vv] && edge[i].f > 0){
que.push(vv);
dis[vv] = dis[fro] + 1;
}
}
}
return dis[T];
}
int dfs(int x, int rest){
if(!rest || x == T) return rest;
for(int& i = cur[x], vv; ~i; i = edge[i].next){
vv = edge[i].v;
if(dis[vv] == dis[x] + 1 && edge[i].f > 0){
int d = dfs(vv, min(rest, edge[i].f));
if(d > 0){
edge[i].f -= d;
edge[i ^ 1].f += d;
return d;
}
}
}
return 0;
}
void dinic(){
mf = 0;
while(bfs()){
//for(int i = 1; i <= T; ++i) printf("%d %d\n", i, dis[i]);
for(int i = 1; i <= T; ++i) cur[i] = head[i];
mf += dfs(S, inf);
//printf("mf = %d\n", mf);
}
}
int main(){
memset(head, -1, sizeof(head));
scanf("%d%d", &n, &m);
for(int i = 1, x, y; i <= m; ++i){
scanf("%d%d%d%d", &x, &y, &low[i], &upp[i]);
addedge(x, y, upp[i] - low[i]);
out[x] += low[i];
in[y] += low[i];
}
S = n + 1, T = n + 2;
for(int i = 1; i <= n; ++i){
if(in[i] > out[i]){
addedge(S, i, in[i] - out[i]);
cnt += in[i] - out[i];
}
else if(out[i] > in[i]) addedge(i, T, out[i] - in[i]);
}
dinic();
if(mf == cnt){
printf("YES\n");
for(int i = 1; i <= m; ++i){
printf("%d\n", low[i] + edge[(i << 1) - 1].f);
}
}
else printf("NO\n");
return 0;
}
有源汇有上下界最大流
在原图上再跑一次最大流就好啦
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <queue>
using namespace std;
const int N = 205;
const int M = 1e6;
const int inf = 0x3f3f3f3f;
struct Edge{
int v, f, next;
}edge[M];
int head[N], cur[N], esize = -1;
inline void addedge(int x, int y, int z){
//printf("%d %d %d\n", x, y, z);
edge[++esize] = (Edge){y, z, head[x]};
head[x] = esize;
edge[++esize] = (Edge){x, 0, head[y]};
head[y] = esize;
}
int n, m, S, T, SS, TT, cnt, mf;
int low[M], upp[M], d[N];
int dis[N], fro;
queue<int> que;
bool bfs(int s, int t){
memset(dis, 0, sizeof(dis));
que.push(s); dis[s] = 1;
while(!que.empty()){
fro = que.front(); que.pop();
for(int i = head[fro], vv; ~i; i = edge[i].next){
vv = edge[i].v;
if(!dis[vv] && edge[i].f > 0){
que.push(vv);
dis[vv] = dis[fro] + 1;
}
}
}
//printf("%d\n", dis[T]);
return dis[t];
}
int dfs(int x, int rest, int t){
// printf("%d %d\n", x, rest);
if(!rest || x == t) return rest;
for(int& i = cur[x], vv; ~i; i = edge[i].next){
vv = edge[i].v;
if(dis[vv] == dis[x] + 1 && edge[i].f > 0){
int d = dfs(vv, min(rest, edge[i].f), t);
if(d > 0){
//printf("%d\n", d);
edge[i].f -= d;
edge[i ^ 1].f += d;
return d;
}
}
}
return 0;
}
void dinic(int s, int t){
mf = 0;
while(bfs(s, t)){
//for(int i = 1; i <= T; ++i) printf("%d %d\n", i, dis[i]);
memcpy(cur, head, sizeof(cur));
mf += dfs(s, inf, t);
//printf("mf = %d\n", mf);
}
}
int main(){
memset(head, -1, sizeof(head));
scanf("%d%d%d%d", &n, &m, &S, &T);
for(int i = 1, x, y; i <= m; ++i){
scanf("%d%d%d%d", &x, &y, &low[i], &upp[i]);
addedge(x, y, upp[i] - low[i]);
d[x] -= low[i];
d[y] += low[i];
}
//printf("*");
SS = n + 1, TT = n + 2;
for(int i = 1; i <= n; ++i){
if(d[i] > 0){
addedge(SS, i, d[i]);
cnt += d[i];
}
else if(d[i] < 0) addedge(i, TT, -d[i]);
}
addedge(T, S, inf);
int rec = esize;
dinic(SS, TT);
if(mf != cnt){
printf("please go home to sleep\n");
return 0;
}
head[S] = edge[head[S]].next;
head[T] = edge[head[T]].next;
dinic(S, T); mf += edge[rec].f;
printf("%d", mf);
return 0;
}
有源汇有上下界最小流
1.从SS到TT跑最大流
2.从T到S连一条大小为INF的边
3.从SS到TT跑最大流
4.最后T到S的流量就是最小流
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <queue>
using namespace std;
const int N = 5e4 + 9;
const int M = 1e6;
const int inf = 0x3f3f3f3f;
struct Edge{
int v, f, next;
}edge[M];
int head[N], cur[N], esize = -1;
inline void addedge(int x, int y, int z){
//printf("%d %d %d\n", x, y, z);
edge[++esize] = (Edge){y, z, head[x]};
head[x] = esize;
edge[++esize] = (Edge){x, 0, head[y]};
head[y] = esize;
}
int n, m, S, T, SS, TT, cnt, mf;
int low[M], upp[M], d[N];
int dis[N], fro;
queue<int> que;
bool bfs(int s, int t){
memset(dis, 0, sizeof(dis));
que.push(s); dis[s] = 1;
while(!que.empty()){
fro = que.front(); que.pop();
for(int i = head[fro], vv; ~i; i = edge[i].next){
vv = edge[i].v;
if(!dis[vv] && edge[i].f > 0){
que.push(vv);
dis[vv] = dis[fro] + 1;
}
}
}
//printf("%d\n", dis[T]);
return dis[t];
}
int dfs(int x, int rest, int t){
// printf("%d %d\n", x, rest);
if(!rest || x == t) return rest;
int tmp = 0;
for(int& i = cur[x], vv; ~i; i = edge[i].next){
vv = edge[i].v;
if(dis[vv] == dis[x] + 1 && edge[i].f > 0){
int d = dfs(vv, min(rest - tmp, edge[i].f), t);
edge[i].f -= d;
edge[i ^ 1].f += d;
tmp += d;
if(tmp == rest) return tmp;
}
}
return tmp;
}
void dinic(int s, int t){
while(bfs(s, t)){
//for(int i = 1; i <= T; ++i) printf("%d %d\n", i, dis[i]);
memcpy(cur, head, sizeof(cur));
mf += dfs(s, inf, t);
//printf("mf = %d\n", mf);
}
}
int main(){
memset(head, -1, sizeof(head));
scanf("%d%d%d%d", &n, &m, &S, &T);
for(int i = 1, x, y; i <= m; ++i){
scanf("%d%d%d%d", &x, &y, &low[i], &upp[i]);
addedge(x, y, upp[i] - low[i]);
d[x] -= low[i];
d[y] += low[i];
}
//printf("*");
SS = n + 1, TT = n + 2;
for(int i = 1; i <= n; ++i){
if(d[i] > 0){
addedge(SS, i, d[i]);
cnt += d[i];
}
else if(d[i] < 0) addedge(i, TT, -d[i]);
}
dinic(SS, TT);
addedge(T, S, inf);
int rec = esize;
dinic(SS, TT);
if(mf != cnt){
printf("please go home to sleep\n");
return 0;
}
else printf("%d", edge[rec].f);
return 0;
}