网络流的模型和应用

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\n",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\n",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_x\geq out_x\),这就意味x点要多向外输出\(in_x\text{-}out_x\)的流量,我们从S向x连\(in_x\text{-}out_x\)容量的边。
  • \(in_x\leq out_x\),这就意味x点要多从外输入\(out_x\text{-}in_x\)的流量,我们从x向T连\(out_x\text{−}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\n");return 0;}
	printf("YES\n");
	for (int i = 2;i <= 2*m;i+=2) printf("%lld\n",ed[i^1].w+e[i/2].down);
	return 0;
}

6. 有源汇上下界最大流

初始从\(t\)\(s\)连一条[0,inf]的边,再把两个点和普通点一起向虚构源点和汇点仿无源连边,跑最大流判断是否有解,再从真实的\(s\Rightarrow 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\n");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\n",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\n");return 0;}
    printf("%lld\n",ed[tot].w);
	return 0;
}

8. 图的模型

posted @ 2021-02-02 13:31  小又又yyyy  阅读(175)  评论(0编辑  收藏  举报