1. 最大流
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <queue>
#define int long long
using namespace std;
int read(){
int x = 1,a = 0;char ch = getchar();
while (ch < '0'||ch > '9'){if (ch == '-') x = -1;ch = getchar();}
while (ch >= '0'&&ch <= '9'){a = a*10+ch-'0';ch = getchar();}
return x*a;
}
const int maxn = 1e6+10,inf = 1e18+7;
int n,m,s,t;
int head[maxn],tot = 1,dis[maxn],cur[maxn];
struct node{
int to,nxt,w;
}ed[maxn*2];
void add(int u,int to,int w){
ed[++tot].w = w;
ed[tot].to = to;
ed[tot].nxt = head[u];
head[u] = tot;
}
bool BFS(){
queue<int> q;q.push(s);
memset(dis,0,sizeof(dis));dis[s] = 1;
while (!q.empty()){
int x = q.front();q.pop();
for (int i = head[x];i;i = ed[i].nxt){
int to = ed[i].to;
if (dis[to]||!ed[i].w) continue;
dis[to] = dis[x]+1;
q.push(to);
}
}
return dis[t];
}
int DFS(int x,int lim){
if (!lim||x == t) return lim;
int res = 0;
for (int &i = head[x];i;i = ed[i].nxt){
int to = ed[i].to;
if (dis[to] != dis[x]+1) continue;
int tmp = DFS(to,min(lim,ed[i].w));
lim -= tmp,ed[i].w -= tmp,ed[i^1].w += tmp,res += tmp;
if (!lim) break;
}
return res;
}
signed main(){
n = read(),m = read(),s = read(),t = read();
for (int i = 1;i <= m;i++){
int u = read(),v = read(),w = read();
add(u,v,w),add(v,u,0);
}
for (int i = 0;i <= tot;i++) cur[i] = head[i];
int ans = 0,tmp;
while (BFS()){
while (tmp = DFS(s,inf)) ans += tmp;
for (int i = 0;i <= tot;i++) head[i] = cur[i];
}
printf("%lld
",ans);
return 0;
}
2. 费用流
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <queue>
#define ll long long
using namespace std;
ll read(){
int x = 1,a = 0;char ch = getchar();
while (ch < '0'||ch > '9'){if (ch == '-') x = -1;ch = getchar();}
while (ch >= '0'&&ch <= '9'){a = a*10+ch-'0';ch = getchar();}
return x*a;
}
const ll maxn = 1e6+10,inf = 1e18+7;
ll n,m,s,t;
ll head[maxn],tot = 1,dis[maxn],cur[maxn],vis[maxn],pre1[maxn],pre2[maxn];
struct node{
ll to,nxt,w,lim;
}ed[maxn*2];
void add(ll u,ll to,ll lim,ll w){
ed[++tot].w = w;
ed[tot].to = to;
ed[tot].nxt = head[u];
ed[tot].lim = lim;
head[u] = tot;
}
bool SPFA(){
memset(vis,0,sizeof(vis));
for (int i = s;i <= t;i++) dis[i] = inf;
queue<ll> q;q.push(s);
dis[s] = 0,vis[s] = 1;
while (!q.empty()){
int x = q.front();q.pop();
vis[x] = 0;
for (int i = head[x];i;i = ed[i].nxt){
ll to = ed[i].to;
if (!ed[i].lim) continue;
if (dis[to] > dis[x]+ed[i].w){
dis[to] = dis[x]+ed[i].w;
pre1[to] = x,pre2[to] = i;
if (!vis[to]){
vis[to] = 1;
q.push(to);
}
}
}
}
return dis[t] != inf;
}
int main(){
n = read(),m = read();s = 1,t = n;
for (int i = 1;i <= m;i++){
int u = read(),v = read(),w = read(),lim = read();
add(u,v,w,lim),add(v,u,0,-lim);
}
ll ans1 = 0,ans2 = 0;
while (SPFA()){
ll tmp = inf;
for (int i = t;i != s;i = pre1[i]) tmp = min(tmp,ed[pre2[i]].lim);
ans1 += tmp,ans2 += tmp*dis[t];
for (int i = t;i != s;i = pre1[i]) ed[pre2[i]].lim -= tmp,ed[pre2[i]^1].lim += tmp;
}
printf("%lld %lld
",ans1,ans2);
return 0;
}
3. 最小割
最小割 = 最大流
4. 最大权闭合子图
这个问题可以转化为最小割问题,用网络流解决。
从源点s向每个正权点连一条容量为权值的边,每个负权点向汇点t连一条容量为权值的绝对值的边,有向图原来的边容量全部为无限大。求它的最小割,割掉后,与源点s连通的点构成最大权闭合子图,权值为(正权值之和-最小割)
5. 无源汇上下界可行流
记第i条边的下界为(down_i),上界为(up_i)。
我们先让每条边流下界的流量,即将每条边i的容量设为(up_i−down_i),下界为0,现在我们能满足下界的要求了,但是流量是不守恒的,建虚拟源点S和汇点T。
我们记每个点x的入流量为(in_x),出流量为(out_x),之后我们根据(in_x)和(out_x)的大小分类讨论:
- (in_xgeq out_x),这就意味x点要多向外输出(in_x ext{-}out_x)的流量,我们从S向x连(in_x ext{-}out_x)容量的边。
- (in_xleq out_x),这就意味x点要多从外输入(out_x ext{-}in_x)的流量,我们从x向T连(out_x ext{−}in_x)容量的边。
之后我们求最大流,如果S流出的边有不满流的,就无解。
对于边i,它的真实流量就是下界+这条边流过的流量(即反边流量)。
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <queue>
#define int long long
using namespace std;
int read(){
int x = 1,a = 0;char ch = getchar();
while (ch < '0'||ch > '9'){if (ch == '-') x = -1;ch = getchar();}
while (ch >= '0'&&ch <= '9'){a = a*10+ch-'0';ch = getchar();}
return x*a;
}
const int maxn = 1e6+10,inf = 1e18+7;
int n,m,s,t;
int head[maxn],tot = 1,dis[maxn],cur[maxn],sum;
struct ask{
int u,v,up,down;
}e[maxn];
int in[maxn],out[maxn];
struct node{
int to,nxt,w;
}ed[maxn*2];
void add(int u,int to,int w){
ed[++tot].w = w;
ed[tot].to = to;
ed[tot].nxt = head[u];
head[u] = tot;
}
bool BFS(){
queue<int> q;q.push(s);
memset(dis,0,sizeof(dis));dis[s] = 1;
while (!q.empty()){
int x = q.front();q.pop();
for (int i = head[x];i;i = ed[i].nxt){
int to = ed[i].to;
if (dis[to]||!ed[i].w) continue;
dis[to] = dis[x]+1;
q.push(to);
}
}
return dis[t];
}
int DFS(int x,int lim){
if (!lim||x == t) return lim;
int res = 0;
for (int &i = head[x];i;i = ed[i].nxt){
int to = ed[i].to;
if (dis[to] != dis[x]+1) continue;
int tmp = DFS(to,min(lim,ed[i].w));
lim -= tmp,ed[i].w -= tmp,ed[i^1].w += tmp,res += tmp;
if (!lim) break;
}
return res;
}
signed main(){
n = read(),m = read(),s = 0,t = n+1;
for (int i = 1;i <= m;i++) e[i].u = read(),e[i].v = read(),e[i].down = read(),e[i].up = read();
for (int i = 1;i <= m;i++) in[e[i].v] += e[i].down,out[e[i].u] += e[i].down;
for (int i = 1;i <= m;i++) add(e[i].u,e[i].v,e[i].up-e[i].down),add(e[i].v,e[i].u,0);
for (int i = 1;i <= n;i++){
if (in[i] < out[i]) add(i,t,out[i]-in[i]),add(t,i,0);
if (in[i] > out[i]) add(s,i,in[i]-out[i]),add(i,s,0),sum += in[i]-out[i];
}
for (int i = 0;i <= tot;i++) cur[i] = head[i];
int ans = 0,tmp;
while (BFS()){
while (tmp = DFS(s,inf)) ans += tmp;
for (int i = 0;i <= tot;i++) head[i] = cur[i];
}
if (ans != sum) {printf("NO
");return 0;}
printf("YES
");
for (int i = 2;i <= 2*m;i+=2) printf("%lld
",ed[i^1].w+e[i/2].down);
return 0;
}
6. 有源汇上下界最大流
初始从(t)到(s)连一条[0,inf]的边,再把两个点和普通点一起向虚构源点和汇点仿无源连边,跑最大流判断是否有解,再从真实的(sRightarrow t)跑最大流求解
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <queue>
#define int long long
using namespace std;
int read(){
int x = 1,a = 0;char ch = getchar();
while (ch < '0'||ch > '9'){if (ch == '-') x = -1;ch = getchar();}
while (ch >= '0'&&ch <= '9'){a = a*10+ch-'0';ch = getchar();}
return x*a;
}
const int maxn = 1e6+10,inf = 1e18+7;
int n,m,s,t,st,sd,ans;
int head[maxn],tot = 1,dis[maxn],cur[maxn],sum;
struct ask{
int u,v,up,down;
}e[maxn];
int in[maxn],out[maxn];
struct node{
int to,nxt,w;
}ed[maxn*2];
void add(int u,int to,int w){
ed[++tot].w = w;
ed[tot].to = to;
ed[tot].nxt = head[u];
head[u] = tot;
}
bool BFS(){
queue<int> q;q.push(s);
memset(dis,0,sizeof(dis));dis[s] = 1;
while (!q.empty()){
int x = q.front();q.pop();
for (int i = head[x];i;i = ed[i].nxt){
int to = ed[i].to;
if (dis[to]||!ed[i].w) continue;
dis[to] = dis[x]+1;
q.push(to);
}
}
return dis[t];
}
int DFS(int x,int lim){
if (!lim||x == t) return lim;
int res = 0;
for (int &i = head[x];i;i = ed[i].nxt){
int to = ed[i].to;
if (dis[to] != dis[x]+1) continue;
int tmp = DFS(to,min(lim,ed[i].w));
lim -= tmp,ed[i].w -= tmp,ed[i^1].w += tmp,res += tmp;
if (!lim) break;
}
return res;
}
signed main(){
n = read(),m = read(),st = read(),sd = read();s = 0,t = n+1;
for (int i = 1;i <= m;i++) e[i].u = read(),e[i].v = read(),e[i].down = read(),e[i].up = read();
for (int i = 1;i <= m;i++) in[e[i].v] += e[i].down,out[e[i].u] += e[i].down;
for (int i = 1;i <= m;i++) add(e[i].u,e[i].v,e[i].up-e[i].down),add(e[i].v,e[i].u,0);
for (int i = 1;i <= n;i++){
if (in[i] < out[i]) add(i,t,out[i]-in[i]),add(t,i,0);
if (in[i] > out[i]) add(s,i,in[i]-out[i]),add(i,s,0),sum += in[i]-out[i];
}
add(sd,st,inf),add(st,sd,0);
for (int i = 0;i <= tot;i++) cur[i] = head[i];
int tmp;
while (BFS()){
while (tmp = DFS(s,inf)) ans += tmp;
for (int i = 0;i <= tot;i++) head[i] = cur[i];
}
if (ans < sum) {printf("please go home to sleep
");return 0;}
s = st,t = sd,ans = ed[tot].w;
ed[tot].w = ed[tot^1].w = 0;
while (BFS()){
while (tmp = DFS(s,inf)) ans += tmp;
for (int i = 0;i <= tot;i++) head[i] = cur[i];
}
printf("%lld
",ans);
return 0;
}
7. 有源汇上下界最小流
和最大流差不多
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <queue>
#define int long long
using namespace std;
int read(){
int x = 1,a = 0;char ch = getchar();
while (ch < '0'||ch > '9'){if (ch == '-') x = -1;ch = getchar();}
while (ch >= '0'&&ch <= '9'){a = a*10+ch-'0';ch = getchar();}
return x*a;
}
const int maxn = 1e6+10,inf = 1e18+7;
int n,m,s,t,st,sd,ans;
int head[maxn],tot = 1,dis[maxn],cur[maxn],sum;
struct ask{
int u,v,up,down;
}e[maxn];
int in[maxn],out[maxn];
struct node{
int to,nxt,w;
}ed[maxn*2];
void add(int u,int to,int w){
ed[++tot].w = w;
ed[tot].to = to;
ed[tot].nxt = head[u];
head[u] = tot;
}
bool BFS(){
queue<int> q;q.push(s);
memset(dis,0,sizeof(dis));dis[s] = 1;
while (!q.empty()){
int x = q.front();q.pop();
for (int i = head[x];i;i = ed[i].nxt){
int to = ed[i].to;
if (dis[to]||!ed[i].w) continue;
dis[to] = dis[x]+1;
q.push(to);
}
}
return dis[t];
}
int DFS(int x,int lim){
if (!lim||x == t) return lim;
int res = 0;
for (int &i = head[x];i;i = ed[i].nxt){
int to = ed[i].to;
if (dis[to] != dis[x]+1) continue;
int tmp = DFS(to,min(lim,ed[i].w));
lim -= tmp,ed[i].w -= tmp,ed[i^1].w += tmp,res += tmp;
if (!lim) break;
}
return res;
}
signed main(){
n = read(),m = read(),st = read(),sd = read();s = 0,t = n+1;
for (int i = 1;i <= m;i++) e[i].u = read(),e[i].v = read(),e[i].down = read(),e[i].up = read();
for (int i = 1;i <= m;i++) in[e[i].v] += e[i].down,out[e[i].u] += e[i].down;
for (int i = 1;i <= m;i++) add(e[i].u,e[i].v,e[i].up-e[i].down),add(e[i].v,e[i].u,0);
for (int i = 1;i <= n;i++){
if (in[i] < out[i]) add(i,t,out[i]-in[i]),add(t,i,0);
if (in[i] > out[i]) add(s,i,in[i]-out[i]),add(i,s,0),sum += in[i]-out[i];
}
for (int i = 0;i <= tot;i++) cur[i] = head[i];
int tmp;
while (BFS()){
while (tmp = DFS(s,inf)) ans += tmp;
for (int i = 0;i <= tot;i++) head[i] = cur[i];
}
add(sd,st,inf),add(st,sd,0);
while (BFS()){
while (tmp = DFS(s,inf)) ans += tmp;
for (int i = 0;i <= tot;i++) head[i] = cur[i];
}
if (ans < sum) {printf("please go home to sleep
");return 0;}
printf("%lld
",ed[tot].w);
return 0;
}