网络流

链接1
链接2
链接3--超全

最大流的增广路算法(最大流)

FF

采用深搜
不停地寻找增广路
每找到一条
就把它减去min
把它的反向边加min
给下次搜索一个反悔的机会

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=210,M=10100;
ll read(){
	ll x=0,f=1;char c=getchar();
	while(c>'9' || c<'0'){
		if(c=='-')f=-1;
		c=getchar();
	}
	while(c>='0' && c<='9'){
		x=(x<<1)+(x<<3)+(c^48);
		c=getchar();
	}
	return x*f;
}
int tot=1,to[M],head[N],last[M],w[M],m,n,s,e,vis[N],match[N];
void add(int u,int v,ll ww){
	to[++tot]=v;
	w[tot]=ww;
	last[tot]=head[u];
	head[u]=tot;
	return;
}
ll dfs(int u,ll flow){
	vis[u]=true;
	if(u==e)return flow;
	for(int i=head[u];i;i=last[i]){
		if(vis[to[i]] || !w[i])continue;
		ll tmp=dfs(to[i],min(flow,(ll)w[i]));
		if(tmp>0){
			w[i]-=tmp;
			w[i^1]+=tmp;
			return tmp;
		}
	}
	return 0;
}
int main(){
	n=read(),m=read(),s=read(),e=read();
	for(int i=1;i<=m;i++){
		int t1=read(),t2=read();ll t3=read();
		add(t1,t2,t3);
		add(t2,t1,0);
	}
	ll ans=0;
	while(1){
		memset(vis,0,sizeof(vis));
		ll tmp=dfs(s,1e18);
		if(tmp==0)break;
		ans+=tmp;
	}
	printf("%lld",ans);
	return 0;
}

EK

bfs版
其他和FF差不多

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=210,M=10100;
ll read(){
	ll x=0,f=1;char c=getchar();
	while(c>'9' || c<'0'){
		if(c=='-')f=-1;
		c=getchar();
	}
	while(c>='0' && c<='9'){
		x=(x<<1)+(x<<3)+(c^48);
		c=getchar();
	}
	return x*f;
}
int tot=1,to[M],head[N],last[M],m,n,s,e,vis[N],match[N],pre[N],pre_rcd[N];ll w[M];
void add(int u,int v,ll ww){
	to[++tot]=v;
	w[tot]=ww;
	last[tot]=head[u];
	head[u]=tot;
	return;
}
bool bfs(){
	queue<int>q;
	memset(vis,0,sizeof(vis));
	memset(pre,0,sizeof(pre));
	memset(pre_rcd,0,sizeof(pre_rcd));
	q.push(s);
	vis[s]=1;
	while(!q.empty()){
		int u=q.front();q.pop();
		for(int i=head[u];i;i=last[i]){
			if(vis[to[i]] || !w[i])continue;
			vis[to[i]]=1;
			pre[to[i]]=u;
			pre_rcd[to[i]]=i;
			if(to[i]==e)
				return true;
			else q.push(to[i]);
		}
	}
	return false;
}
ll EK(){
	ll ans=0;
	while(bfs()){
		ll minn=1e18;
		for(int i=e;i!=s;i=pre[i])
			minn=min(minn,w[pre_rcd[i]]);
		for(int i=e;i!=s;i=pre[i]){
			w[pre_rcd[i]]-=minn;
			w[pre_rcd[i]^1]+=minn;
		}
		ans+=minn;		
	}
	return ans;
}
int main(){
	n=read(),m=read(),s=read(),e=read();
	for(int i=1;i<=m;i++){
		int t1=read(),t2=read();ll t3=read();
		add(t1,t2,t3);
		add(t2,t1,0);
	}
	printf("%lld",EK());
	return 0;
}

dinic

先用bfs把目前图每个点的深度求出来
再用dfs求最短增广路
FF+BFS?
模板题没跑过EK我是没想到的
补充说明:Dinic在跑二分图匹配时比匈牙利快很多
真的吗???

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=210,M=10100;
ll read(){
	ll x=0,f=1;char c=getchar();
	while(c>'9' || c<'0'){
		if(c=='-')f=-1;
		c=getchar();
	}
	while(c>='0' && c<='9'){
		x=(x<<1)+(x<<3)+(c^48);
		c=getchar();
	}
	return x*f;
}
int tot=1,to[M],head[N],last[M],m,n,s,e,dis[N];ll w[M];
void add(int u,int v,ll ww){
	to[++tot]=v;
	w[tot]=ww;
	last[tot]=head[u];
	head[u]=tot;
	return;
}
bool bfs(){
	queue<int>q;
	memset(dis,0x3f,sizeof(dis));
	q.push(s);
	dis[s]=0;
	while(!q.empty()){
		int u=q.front();q.pop();
		for(int i=head[u];i;i=last[i]){
			if(!w[i])continue;
			if(dis[to[i]]>dis[u]+1){
				dis[to[i]]=dis[u]+1;
				q.push(to[i]);
			}
		}
	}
	if(dis[e]!=0x3f3f3f3f)return true;
	return false;
}
ll dfs(int u,ll flow){
	if(u==e)return flow;
	for(int i=head[u];i;i=last[i]){
		if(dis[to[i]]!=dis[u]+1 || !w[i])continue;
		ll tmp=dfs(to[i],min(w[i],flow));
		if(tmp>0){
			w[i]-=tmp;
			w[i^1]+=tmp;
			return tmp;
		}
	}
	return 0;
}
ll dinic(){
	ll ans=0,tmp;
	while(bfs())
		while(tmp=dfs(s,1e18))
			ans+=tmp;
	return ans;
}
int main(){
	n=read(),m=read(),s=read(),e=read();
	for(int i=1;i<=m;i++){
		int t1=read(),t2=read();ll t3=read();
		add(t1,t2,t3);
		add(t2,t1,0);
	}
	printf("%lld",dinic());
	return 0;
}

dinic多路增广优化

上方代码每次dfs只能增广一条路
但只要加上一个used变量
表示目前这个点用了多少流量
如果走完一条增广路
发现还有流量剩余
不如再走一条


因此每次只用dfs一遍
减少了很多重复路径

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=210,M=10100;
ll read(){
	ll x=0,f=1;char c=getchar();
	while(c>'9' || c<'0'){
		if(c=='-')f=-1;
		c=getchar();
	}
	while(c>='0' && c<='9'){
		x=(x<<1)+(x<<3)+(c^48);
		c=getchar();
	}
	return x*f;
}
int tot=1,to[M],head[N],last[M],m,n,s,e,dis[N];ll w[M];
void add(int u,int v,ll ww){
	to[++tot]=v;
	w[tot]=ww;
	last[tot]=head[u];
	head[u]=tot;
	return;
}
bool bfs(){
	queue<int>q;
	memset(dis,0x3f,sizeof(dis));
	q.push(s);
	dis[s]=0;
	while(!q.empty()){
		int u=q.front();q.pop();
		for(int i=head[u];i;i=last[i]){
			if(!w[i])continue;
			if(dis[to[i]]>dis[u]+1){
				dis[to[i]]=dis[u]+1;
				q.push(to[i]);
			}
		}
	}
	if(dis[e]!=0x3f3f3f3f)return true;
	return false;
}
ll dfs(int u,ll flow){
	ll used=0;
	if(u==e)return flow;
	for(int i=head[u];i;i=last[i]){
		if(dis[to[i]]!=dis[u]+1 || !w[i])continue;
		ll tmp=dfs(to[i],min(w[i],flow-used));
		if(tmp>0){
			used+=tmp;
			w[i]-=tmp;
			w[i^1]+=tmp;
			if(used==flow)break;
		}
	}
	return used;
}
ll dinic(){
	ll ans=0;
	while(bfs())
		ans+=dfs(s,1e18);
	return ans;
}
int main(){
	n=read(),m=read(),s=read(),e=read();
	for(int i=1;i<=m;i++){
		int t1=read(),t2=read();ll t3=read();
		add(t1,t2,t3);
		add(t2,t1,0);
	}
	printf("%lld",dinic());
	return 0;
}

为什么还是T了一个点

dinic当前弧优化

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=210,M=10100;
ll read(){
	ll x=0,f=1;char c=getchar();
	while(c>'9' || c<'0'){
		if(c=='-')f=-1;
		c=getchar();
	}
	while(c>='0' && c<='9'){
		x=(x<<1)+(x<<3)+(c^48);
		c=getchar();
	}
	return x*f;
}
int tot=1,to[M],head[N],last[M],m,n,s,e,dis[N];ll w[M],cur[N];
void add(int u,int v,ll ww){
	to[++tot]=v;
	w[tot]=ww;
	last[tot]=head[u];
	head[u]=tot;
	return;
}
bool bfs(){
	queue<int>q;
	memset(dis,0,sizeof(dis));
	memcpy(cur,head,sizeof(head));
	q.push(s);
	dis[s]=1;
	while(!q.empty()){
		int u=q.front();q.pop();
		for(int i=head[u];i;i=last[i]){
			if(!w[i] || dis[to[i]])continue;
			dis[to[i]]=dis[u]+1;
        if(to[i]==e)return true;
			q.push(to[i]);
		}
	}
	return false;
}
ll dfs(int u,ll flow){
	ll used=0;
	if(u==e)return flow;
	for(int i=cur[u];i;i=last[i]){
		cur[u]=i;
		if(dis[to[i]]!=dis[u]+1 || !w[i])continue;
		ll tmp=dfs(to[i],min(w[i],flow-used));
		if(tmp>0){
			used+=tmp;
			w[i]-=tmp;
			w[i^1]+=tmp;
			if(used==flow)break;
		}
	}
	return used;
}
ll dinic(){
	ll ans=0;
	while(bfs())
		ans+=dfs(s,1e18);
	return ans;
}
int main(){
	n=read(),m=read(),s=read(),e=read();
	for(int i=1;i<=m;i++){
		int t1=read(),t2=read();ll t3=read();
		add(t1,t2,t3);
		add(t2,t1,0);
	}
	printf("%lld",dinic());
	return 0;
}

速度暴增

ISAP

只用跑一遍BFS
dfs时顺便修改了深度

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=210,M=10100;
ll read(){
	ll x=0,f=1;char c=getchar();
	while(c>'9' || c<'0'){
		if(c=='-')f=-1;
		c=getchar();
	}
	while(c>='0' && c<='9'){
		x=(x<<1)+(x<<3)+(c^48);
		c=getchar();
	}
	return x*f;
}
int tot=1,to[M],head[N],last[M],m,n,s,e,dep[N],cur[N],gap[N];ll w[M];
void add(int u,int v,ll ww){
	to[++tot]=v;
	w[tot]=ww;
	last[tot]=head[u];
	head[u]=tot;
	return;
}
void bfs(){
	queue<int>q;
	q.push(e);
	dep[e]=1;
	gap[1]=1;
	while(!q.empty()){
		int u=q.front();q.pop();
		for(int i=head[u];i;i=last[i]){
			if(dep[to[i]])continue;
			dep[to[i]]=dep[u]+1;
			gap[dep[to[i]]]++;
			q.push(to[i]);
		}
	}
	return;//bfs不能提前跳出(dinic可以)
}
ll dfs(int u,ll flow){
	ll used=0;
	if(u==e)return flow;
	for(int i=cur[u];i;i=last[i]){
		cur[u]=i;
		if(dep[to[i]]!=dep[u]-1 || !w[i])continue;
		ll tmp=dfs(to[i],min(w[i],flow-used));
		if(tmp>0){
			used+=tmp;
			w[i]-=tmp;
			w[i^1]+=tmp;
			if(used==flow)return used;//一定要跳??
		}
	}
	gap[dep[u]]--;
	if(gap[dep[u]]==0)dep[s]=n+1;//n是点的数量。
	dep[u]++;
	gap[dep[u]]++;
	return used;
}
ll ISAP(){
	ll ans=0;
	bfs();
	while(dep[s]<=n){
		memcpy(cur,head,sizeof(cur));
		ans+=dfs(s,1e18);
	}
	return ans;
}
int main(){
	n=read(),m=read(),s=read(),e=read();
	for(int i=1;i<=m;i++){
		int t1=read(),t2=read();ll t3=read();
		add(t1,t2,t3);
		add(t2,t1,0);
	}
	printf("%lld",ISAP());
	return 0;
}

最小费用最大流

dinic

#include<bits/stdc++.h>
using namespace std;
const int N=59,M=3010,S=52,E=53,inf=1e9;
int read(){
	int x=0,f=1;char c=getchar();
	while(c>'9' || c<'0'){if(c=='-')f=-1;c=getchar();}
	while(c>='0' && c<='9'){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
	return x*f;
}
int n,ans,sumva,m,SS;

int head[N],tot=1,last[M],liu[M],va[M],to[M],cur[N];
int vis[N];
int dis[N];
void add(int u,int v,int l,int a){
	to[++tot]=v,va[tot]=a,liu[tot]=l,last[tot]=head[u],head[u]=tot;
	to[++tot]=u,va[tot]=-a,liu[tot]=0,last[tot]=head[v],head[v]=tot;
	return;
}
bool spfa(){
	memset(dis,0x3f,sizeof(dis));
	dis[S]=0;queue<int>q;q.push(S);
	while(!q.empty()){
		int u=q.front();q.pop();
		for(int i=head[u];i;i=last[i]){
			int v=to[i];if(!liu[i])continue;
			if(dis[v]>dis[u]+va[i]){
				dis[v]=dis[u]+va[i];
				q.push(v);
			}
		}
	}
	if(dis[E]!=0x3f3f3f3f)return true;
	return false;
}
int dfs(int u,int flow){
	vis[u]=1;
	if(u==E)return flow;
	int used=0;
	for(int i=cur[u];i;i=last[i]){cur[u]=i;
		int v=to[i];if(vis[v] && v!=E || dis[v]!=dis[u]+va[i] || !liu[i])continue;
		int tmp=dfs(v,min(liu[i],flow-used));
		if(tmp){
			sumva+=va[i]*tmp;
			liu[i]-=tmp;
			liu[i^1]+=tmp;
			used+=tmp;
			if(used==flow)break;
		}
	}
	return used;
}
void dinic(){
	while(spfa()){
		do{
			memcpy(cur,head,sizeof(head));
			memset(vis,0,sizeof(vis));
			ans+=dfs(S,inf);
		}while(vis[E]==1);
	}
	return;
}
int bk[N];
void pr(int u){
	if(bk[u])return;
	bk[u]=1;
	cout<<u<<endl;
	for(int i=head[u];i;i=last[i])cout<<to[i]<<" "<<liu[i]<<" "<<va[i]<<endl;
	cout<<endl;
	for(int i=head[u];i;i=last[i]){
		if(liu[i])pr(to[i]);
	}
	return;
}
int main(){
	n=read();m=read(),SS=read();
	//read()
	dinic();
	printf("%d\n",sumva);
	return 0;
}

最大流最小割定理

最大流最小割定理是网络流理论的重要定理。是指在一个网络流中,能够从源点到达汇点的最大流量等于如果从网络中移除就能够导致网络流中断的边的集合的最小容量和。即在任何网络中,最大流的值等于最小割的容量。

关于怎么求最小割的方案

对于一张图,如果一条边割掉之后其最大流与原来的差值等于边流量,则这条边是必经边,也就是割边。

posted @   FJOI  阅读(55)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· AI 智能体引爆开源社区「GitHub 热点速览」
· 写一个简单的SQL生成工具
· Manus的开源复刻OpenManus初探
点击右上角即可分享
微信分享提示