最大流

某位朋友催更,于是写这篇文章
今天我们讲最大流。

〇、题目&模型

Link
题目的意思就是说从自来水厂到你家要发水,要经过各个站点,而每一条有向边上都有容量,表示从边那头总共最多发这么多的容量。

一、思路(dinic)

可惜我只会dinic......
首先,如果自来水厂不发水,那肯定是可以的一种方案,叫做零流
接下来,交了两倍水费的你打开了水龙头,于是自来水厂开始疯狂运水。
就拿这张图:image
其中4是源点,3是汇点。

1.犯难的水

自来水厂把水发了出去,此时,水为难了。他可以走2也可以走3,走哪个呢?
自来水厂不想多考虑,选择:“走编号小的那个!”
在我们看来,其实他是一个DFS,走完小的走大的。不过,我们走完之后就让我们走的路容量减去我们流量。如果流量超过了容量,就只走容量那么多。
于是,水选择4->2->1->3,30流量。
1无路可走,退回2。
2没有流量了,退回4。
一直到这时,4才走到3,20流量。
总共走到了50流量。
巨佬的话来讲:image

2.犯错的水

那么,接下来是一个最最最最最经典的最大流图片了:image
我们第一次找到了1->2->3->4这条增广路,这条路上的流量值显然是1。
于是我们修改后得到了下面这个流。(图中的数字是剩余容量)
image
这时候1,2和3,4边上的容量都用完了,我们再也找不到其他的路了,当前的流量是1。
但是,这个答案明显不是最大流,因为我们可以同时走1->2->4和1->3->4,这样可以得到流量为2的流。
问题就出在我们没有给他一个撤销的机会。
因此,我们……加反向边!
输入时,把每一条边都加上反向边,容量相同,就可以“撤销”这一步了(从当前位置回去,流量不变)。

二、代码

照着image写的代码:

#include<bits/stdc++.h>
using namespace std;
#define int long long
int n,m,s,t;
struct edge{//一条边
	int from,to,c,f;
	edge(int u,int v,int cc,int ff):from(u),to(v),c(cc),f(ff){}
};
vector<edge> e;
vector<int> G[205];
int d[205],cur[205];
bool vis[205];
void init(int n){//初始化
	for(int i=1;i<=n;i++) G[i].clear();
	e.clear();
}
void add(int from,int to,int ccc){//加边
	e.push_back(edge(from,to,ccc,0));
	e.push_back(edge(to,from,0,0));
	m=e.size();
	G[from].push_back(m-2);
	G[to].push_back(m-1);
}
bool bfs(){//判断是否能走
	memset(vis,0,sizeof(vis));
	queue<int> q;
	q.push(s);
	d[s]=0;
	vis[s]=1;
	while(!q.empty()){
		int x=q.front();
		q.pop();
		for(int i=0;i<G[x].size();i++){//有边可走
			edge& ed=e[G[x][i]];
			if(!vis[ed.to]&&ed.c>ed.f){//能够走到
				vis[ed.to]=1;
				d[ed.to]=d[x]+1;
				q.push(ed.to);
			}
		}
	}
	return vis[t];//还有可行流
}
int DFS(int x,int a){//开始计算
	if(x==t||!a) return a;
	int flow=0,F;
	for(int& i=cur[x];i<G[x].size();i++){
		edge& ee=e[G[x][i]];
		if(d[x]+1==d[ee.to]&&(F=DFS(ee.to,min(a,ee.c-ee.f)))>0){//走边
			ee.f+=F;
			e[G[x][i]^1].f-=F;
			flow+=F;
			a-=F;
			if(a==0) break;
		}
	}
	return flow;
}
signed main(){
	cin>>n>>m>>s>>t;
	int x=m;
	init(n);
	int a,b,ca;
	for(int i=1;i<=x;i++){
		cin>>a>>b>>ca;
		add(a,b,ca);
	}
	int flow=0;
	while(bfs()){//计算最大流
		memset(cur,0,sizeof(cur));
		flow+=DFS(s,0x3f3f3f3f3f3f3f3f);
	}
	cout<<flow<<endl;
	return 0;
}

那么今天就讲到这里,我们下次再见~~~

posted @ 2021-08-05 13:40  cyx001  阅读(78)  评论(3编辑  收藏  举报