浅谈网络流

并不会从零开始讲网络流 , 并且其中很多是个人理解.


\(①\) \(:\) 最大流 \(\cdot\) \(DK\)

每次去找 一条 (注意是一条) 路增广 , 再去更新.

\(vis\) 保证每次每个点只找到一次 , 也防止双向边成环卡死.

一定不能用 \(flow\) 单个变量去记录最小流 , 要用数组 \(flow\) 去更新 , 因为在一条路的路程中会产生最小流 , 但这条路不一定走得通.


关于反向边的建立 , 我认为就是让能流的残余流量流完

image

很显然答案是 : 198.

但若我们走的不是 "显然" 的路 , 走 \(1 \rightarrow 2 \rightarrow 3 \rightarrow 4\) , 则会变成 :

image

这一次我们只能走 \(1 \rightarrow 3 \rightarrow 2 \rightarrow 4\) , 则会变成 :

image

三个图总体来看发现 : \(2 - 3\) 反向边的建立让我们两条显然的路都经过了流量 \(1\) .


#include<iostream>
#include<cstring>
#include<cstdio>
#include<queue>
using namespace std;
const int N = 201;
const int M = 5001;
const int inf = 2147483647;
long long ans;
int n,m,s,t,cnt = 1;
int head[N],vis[N],pre[N],flow[N];
struct bian{
	int to,v,next;
}len[M<<1];
void add(int from,int to,int v){
	len[++cnt].v = v;
	len[cnt].to = to;
	len[cnt].next = head[from];
	head[from] = cnt;
}
bool bfs(){
	queue<int> Q;
	flow[s] = inf;
	memset(vis,0,sizeof(vis));
	Q.push(s) , vis[s] = 1;
	while(Q.size()){
		int now = Q.front();Q.pop();
		for(int k=head[now];k;k=len[k].next){
			int to = len[k].to , v = len[k].v;
			if(!vis[to]&&v){
				//flow = min(flow,v);
				flow[to] = min(v,flow[now]);
				pre[to] = k;
				Q.push(to),vis[to] = 1;
				if(to == t) return true;
			}
		}
	}
	return false;
}
void updata(){
	int x = t;
	while(x != s){
		int id = pre[x];
		len[id].v -= flow[t];
		len[id^1].v += flow[t];
		x = len[id^1].to;
	}
	ans += flow[t];
}
int main()
{
	scanf("%d%d%d%d",&n,&m,&s,&t);
	for(int i=1;i<=m;++i){
		int x,y,v;scanf("%d%d%d",&x,&y,&v);
		add(x,y,v),add(y,x,0);
	}
	while(bfs()) updata();
	printf("%lld",ans);
	return 0;
}

\(②\) \(:\) 最大流 \(\cdot\) \(Dinic\)

分层数组 \(dep\) : 保证能多路增广 , 且防止双向边成环卡死.

如图 : \(1 \rightarrow 2 \rightarrow 4\) , \(1 \rightarrow 3 \rightarrow 4\) , 两条路能在一次 \(dfs\) 中完成增广 , 而不是像 \(DK\) 那次被 \(vis\) 数组限定每次只能选一条.

image


当前弧优化 \(now\) 数组 : 当一条路进行了增广后 , 我们没必要在同一次 \(dfs\) 中对其再增广 , 不好解释看图吧.

image

当我们走了 \(1 \rightarrow 2 \rightarrow 4 \rightarrow 6\) , \(1 \rightarrow 2 \rightarrow 4 \rightarrow 5\) 时 ,

下一次就只需走 \(1 \rightarrow 3 \rightarrow 4 \rightarrow 7\) 即可 , 从 \(6\) , \(5\) 走一定会遇到 边权为 \(0\) 边.

注意我们用 &k = now[x] 进行对 \(now\) 数组的实时更新.


小技巧优化 : nowflow < flow , nowflow += nodeflow.

结合图与递归就能理解了 :

image


#include<iostream>
#include<cstring>
#include<cstdio>
#include<queue>
using namespace std;
const int N = 201;
const int M = 5001;
const int inf = 2147482647;
long long ans;
int n,m,s,t,cnt = 1;
int head[N],now[N],dep[N];
struct bian{
	int to,next,v;
}len[M<<1];
void add(int from,int to,int v){
	len[++cnt].v = v;
	len[cnt].to = to;
	len[cnt].next = head[from];
	head[from] = cnt;
}
bool bfs(){
	queue<int> Q;
	memset(dep,0,sizeof(dep));
	Q.push(s) , dep[s] = 1;
	while(Q.size()){
		int pos = Q.front();Q.pop();
		for(int k=head[pos];k;k=len[k].next){
			int to = len[k].to , v = len[k].v;
			if(!dep[to]&&v){
				dep[to] = dep[pos] + 1;
				Q.push(to);
				if(to == t) return true;
			}
		}
	}
	return false;
}
int dfs(int x,int flow){
	if(x == t) return flow;
	int nowflow = 0;
	for(int &k=now[x];k&&nowflow<flow;k=len[k].next){
		int to = len[k].to , v = len[k].v;
		if(dep[to]==dep[x]+1&&v){
			int nodeflow = dfs(to,min(v,flow-nowflow));
			if(nodeflow > 0){
				len[k].v -= nodeflow;
				len[k^1].v += nodeflow;
				nowflow += nodeflow;
			} 
		}
	}
	return nowflow;
}
int main()
{
	scanf("%d%d%d%d",&n,&m,&s,&t);
	for(int i=1;i<=m;++i){
		int x,y,v;scanf("%d%d%d",&x,&y,&v);
		add(x,y,v),add(y,x,0);
	}
	while(bfs()){
		for(int i=1;i<=n;++i)
			now[i] = head[i];
		while(int f = dfs(s,inf))
			ans += f;
	}
	printf("%lld",ans);
	return 0;
}

\(③\) \(:\) 最小费用最大流 \(\cdot\) \(DK\)

在每次我们找残余网络的基础上 (v != 0) , 加入对最短路的判断 .

同时注意我们这里的 \(vis\) 数组在数据弹出时要 \(vis = 0\) , 这在最大流 \(\cdot\) \(DK\) 里面是没有的 , 这里利用的是 \(spfa\) 更新最短路的原理 , 当前有效状态更新并非最优 . 利用 dis[to] > dis[x] + cost 不会陷入双向边成环卡死的现象.

同时注意要用 \(flow\) , \(dis\) 数组去维护最大值.


#include<iostream>
#include<cstring>
#include<cstdio>
#include<queue>
using namespace std;
const int N = 5e3+1;
const int M = 5e4+1;
const int inf = 0x3f3f3f3f;
int n,m,s,t,maxflow,sum,cnt = 1;
int head[N],dis[N],vis[N],flow[N],pre[N];
struct bian{
	int to,next,v,cost;
}len[M<<1]; 
void add(int from,int to,int v,int cost){
	len[++cnt].v = v;
	len[cnt].to = to;
	len[cnt].cost = cost;
	len[cnt].next = head[from];
	head[from] = cnt;
}
bool bfs(){
	queue<int> Q;
	memset(vis,0,sizeof(vis));
	memset(dis,0x3f,sizeof(dis));
	Q.push(s);
	vis[s] = 1 , dis[s] = 0 , flow[s] = inf;
	while(Q.size()){
		int now = Q.front();Q.pop();
		vis[now] = 0;
		for(int k=head[now];k;k=len[k].next){
			int to = len[k].to , v = len[k].v , cost = len[k].cost;
			if(v&&dis[to] > dis[now] + cost){
				dis[to] = dis[now] + cost;
				pre[to] = k;
				flow[to] = min(flow[now],v);
				if(!vis[to]) Q.push(to),vis[to] = 1;
			}
		}
	}
	return dis[t] != inf;
}
void updata(){
	int x = t;
	while(x != s){
		int id = pre[x];
		len[id].v -= flow[t];
		len[id^1].v += flow[t];
		x = len[id^1].to;
	}
	maxflow += flow[t];
	sum += flow[t]*dis[t];
}
int main()
{
	scanf("%d%d%d%d",&n,&m,&s,&t);
	for(int i=1;i<=m;++i){
		int x,y,v,cost;
		scanf("%d%d%d%d",&x,&y,&v,&cost);
		add(x,y,v,cost) , add(y,x,0,-cost);
	}
	while(bfs()) updata();
	printf("%d %d",maxflow,sum);
	return 0;
}

\(④\) \(:\) 最小费用最大流 \(\cdot\) \(Dinic\)

将分层顺序改为最短路顺序即可.

注意 \(dfs\)\(vis\) 数组 , 因为条件变为 dis[to] == dis[pos] + cost , 并不能防止双向边成环卡死现象 , \(vis\) 为了防止这种情况.

#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<queue>
using namespace std;
const int N = 5e3+1;
const int M = 5e4+1;
const int inf = 0x3f3f3f3f;
int n,m,s,t,maxflow,sum,cnt = 1;
int head[N],now[N],vis[N],dis[N];
struct bian{
	int to,v,next,cost;	
}len[M<<1];
void add(int from,int to,int v,int cost){
	len[++cnt].v = v;
	len[cnt].to = to;
	len[cnt].cost = cost;
	len[cnt].next = head[from];
	head[from] = cnt;
}
bool bfs(){
	queue<int> Q;
	memset(vis,0,sizeof(vis));
	memset(dis,0x3f,sizeof(dis));
	Q.push(s);
	dis[s] = 0 , vis[s] = 1;
	while(Q.size()){
		int pos = Q.front();Q.pop();
		vis[pos] = 0;
		for(int k=head[pos];k;k=len[k].next){
			int to = len[k].to , v = len[k].v , cost = len[k].cost;
			if(v&&dis[to] > dis[pos]+cost){
				dis[to] = dis[pos]+cost;
				if(!vis[to]) Q.push(to),vis[to] = 1;
			}
		}
	}
	return dis[t] != inf;
}
int dfs(int x,int flow){
	if(x == t) return flow;
	vis[x] = 1;
	int nowflow = 0;
	for(int &k=now[x];k&&nowflow<flow;k=len[k].next){
		int to = len[k].to , v = len[k].v , cost = len[k].cost;
		if(v&&!vis[to]&&dis[to]==dis[x]+cost){
			int nodeflow = dfs(to,min(v,flow-nowflow));
			if(nodeflow){
				len[k].v -= nodeflow;
				len[k^1].v += nodeflow;
				sum += nodeflow*cost;
				nowflow += nodeflow;
			}
		}
	}
	vis[x] = 0;
	return nowflow;
}
int main()
{
	scanf("%d%d%d%d",&n,&m,&s,&t);
	for(int i=1;i<=m;++i){
		int x,y,v,cost;
		scanf("%d%d%d%d",&x,&y,&v,&cost);
		add(x,y,v,cost) , add(y,x,0,-cost);
	}
	while(bfs()){
		for(int i=1;i<=n;++i)
			now[i] = head[i];
		while(int f = dfs(s,inf))
			maxflow += f;
	}
	printf("%d %d",maxflow,sum);
	return 0;
}

题目来源 \(:\) 网络最大流 最小费用最大流

posted @ 2021-10-27 22:45  xqy2003  阅读(77)  评论(1)    收藏  举报