把博客园图标替换成自己的图标
把博客园图标替换成自己的图标end

【LOJ117】有源汇有上下界最小流

点此看题面

  • 有一张\(n\)个点\(m\)条边的图。
  • 给你每条边的流量上下界,让你先判断是否存在可行流,若存在则求出从\(s\)\(t\)的最小流。
  • \(n\le50003,m\le125003\)

算是对有源汇网络流的一个复习吧,也不知道为什么之前刷板子的时候会把这道漏了。。。

有源汇网络流

有源汇有上下界最大流类似,我们先从汇点向源点连一条下界为\(0\)、上界为\(INF\)的边(相当于是使原本不满足流量平衡的源汇点也能满足流量平衡,转化成普通点,消去了所谓源汇),跑一遍无源汇有上下界可行流

然后我们记录下添上的这条辅助边的流量并把它删去,就得到了此时可行流中源点到汇点的流量。(有源汇有上下界最大流其实也有这个操作,只不过是直接放在最大流中一起跑了)

想要求出最小流,可以从源点向汇点跑网络流来实现退流,用记下的可行流流量减去退流退掉的流量就是答案了。

代码:\(O(Dinic)\)

#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Rg register
#define RI Rg int
#define Cn const
#define CI Cn int&
#define I inline
#define W while
#define N 50003
#define M 125003
#define LL long long
#define INF (LL)1e18
using namespace std;
int n,m,s,t;
namespace D
{
	#define ss (n+1)
	#define tt (n+2)
	#define adde(x,y,f) (add(x,y,f),add(y,x,0)) 
	#define add(x,y,f) (e[++ee].nxt=lnk[x],e[lnk[x]=ee].to=y,e[ee].F=f)
	#define del(x) (lnk[x]=e[lnk[x]].nxt)
	int ee=1,lnk[N+5],cur[N+5];struct edge {int to,nxt;LL F;}e[2*(N+M)+5];
	int q[N+5],d[N+5];I bool BFS(CI s,CI t) {RI i,k,H,T;for(i=1;i<=tt;++i) d[i]=0;d[q[H=T=1]=s]=1;
		W(H<=T) for(i=lnk[k=q[H++]];i;i=e[i].nxt) e[i].F&&!d[e[i].to]&&(d[q[++T]=e[i].to]=d[k]+1);return d[t];}
	I LL DFS(CI x,CI t,LL f=INF) {if(x==t||!f) return f;LL o,g=0;for(RI& i=cur[x];i;i=e[i].nxt)
		if(d[e[i].to]==d[x]+1&&(o=DFS(e[i].to,t,min(f,e[i].F)))&&(e[i].F-=o,e[i^1].F+=o,g+=o,!(f-=o))) break;return g;}
	LL w[N+5];I void Add(CI x,CI y,Cn LL& l,Cn LL& u) {adde(x,y,u-l),w[x]+=l,w[y]-=l;}//从x向y连一条下界l、上界u的边
	I bool Flow() {RI i;for(i=1;i<=n;++i) w[i]>0&&adde(i,tt,w[i]),w[i]<0&&adde(ss,i,-w[i]);D::Add(t,s,0,INF);//每个点根据供需与超级源汇连边;汇点向源点连辅助边
		W(BFS(ss,tt)) memcpy(cur,lnk,sizeof(lnk)),DFS(ss,tt);for(i=lnk[ss];i;i=e[i].nxt) if(e[i].F) return 0;return 1;}//超级源向超级汇跑最大流,检验是否流满
	I LL MinFlow() {LL g=e[lnk[s]].F;del(s),del(t);W(BFS(t,s)) memcpy(cur,lnk,sizeof(lnk)),g-=DFS(t,s);return g;}//记下辅助边流量并把它删去,从源点向汇点退流
}
int main()
{
	RI i,x,y,l,u;for(scanf("%d%d%d%d",&n,&m,&s,&t),i=1;i<=m;++i) scanf("%d%d%d%d",&x,&y,&l,&u),D::Add(x,y,l,u);
	return D::Flow()?printf("%lld\n",D::MinFlow()):puts("please go home to sleep"),0;//判断是否存在可行流,然后求最小流
}
posted @ 2021-07-21 15:23  TheLostWeak  阅读(58)  评论(0编辑  收藏  举报