有源汇有上下界的最小流(LOJ117)

一个有源汇有上下界的最小流的瞎那啥讲解

其实最小流和最大流个人感觉很类似,连接了超级源点和超级汇点之后跑完一次最大流,一个是再在除去超级源点和超级汇点跑一次最大流,一个是给源汇点连一个无限边求最大流,然后无限边的流量就是答案

因为一个相当于是要把残量网络剩下的流加起来,才是最大流,而这个跑过最大流后剩下的就是最小流了

当然必须要当前弧优化,否则跑不过去

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define re register 
const ll inf=1e15;
int str,des,cnt=1,adj[50050],tp[50050],nxt[500050],to[500050],lev[50050],low[500050],st,de,m,n;
ll def[50050],cap[500050];
inline int read(){
	char ch;
	while((ch=getchar())<'0'||ch>'9'){;}
	int res=ch-'0';
	while((ch=getchar())>='0'&&ch<='9')
	res=res*10+ch-'0';
	return res;
}
inline void addedge(int u,int v,long long p)
{
	nxt[++cnt]=adj[u],adj[u]=cnt,to[cnt]=v,cap[cnt]=p;
	nxt[++cnt]=adj[v],adj[v]=cnt,to[cnt]=u,cap[cnt]=0;
}
inline bool bfs(){
	int u,e,v;
	queue<int> que;
	memset(lev,-1,sizeof(lev));
	que.push(str),lev[str]=0;
	while(!que.empty())
	{
		u=que.front(),que.pop();
		for(int e=adj[u];e;e=nxt[e])
		{
			if(cap[e]>0&&lev[v=to[e]]==-1)
			{
				lev[v]=lev[u]+1,que.push(v);
				if(v==des) return true;
			}
		}
	}
	return false;
}
inline long long dinic( int u,ll flow)
{
	if(u==des) return flow;
	ll res=0,flw;
	int v;
	for(int &e=tp[u];e&&res<=flow;e=nxt[e])
	{
		if(cap[e]>0&&lev[u]+1==lev[v=to[e]])
		{
	  	int minn=dinic(v,min(flow-res,cap[e]));
	  	cap[e]-=minn,cap[e^1]+=minn,res+=minn;
	  	if(flow==res) return res;
		}
	}
	return lev[u]=0,res;
}
inline long long  solve(){
	long long ans=0;
	while(bfs()==true) {
		memcpy(tp,adj,sizeof(adj));
		ans+=dinic(str,inf);
	}
	return ans;
}
int main(){
	n=read(),m=read(),st=read(),de=read();
	int s,t;
	ll up,down;
	long long sum=0;
	for(re int i=1;i<=m;i++)
	{
		s=read(),t=read(),down=read(),up=read();
		addedge(s,t,up-down);
		low[i]=down,def[s]+=down,def[t]-=down;
	}
	str=0,des=n+1;
	for(re int i=1;i<=n;i++)
	{
		if(def[i]>0) addedge(i,des,def[i]);
		if(def[i]<0) addedge(str,i,-def[i]);
	}
	sum=solve();
	addedge(de,st,inf);
	sum+=solve();
	for(re int e=adj[des];e;e=nxt[e]) 
	{
		if(cap[e^1]>0)
		{
			cout<<"please go home to sleep"<<'\n';
			return 0;
		}
	}
	cout<<cap[cnt];
	return 0;
}

转载于:https://www.cnblogs.com/stargazer-cyk/p/10366517.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值