• 【总结&模板】上下界网络流的一些注意点


    以下的内容仅是一些对上下界网络流的补充说明,基础概念都没有涉及,但是有一些注意点。

    这里的三道题(无源汇上下界可行流,有源汇上下界最大流,有源汇上下界最小流)是采用(LOJ)的模板,题号是(115-117)

    (LOJ115) 无源汇有上下界可行流

    题意:

    给定一个无源汇的图,流量有上下界,问一个可行流。

    知识点:

    无源汇有上下界可行流

    解法:

    每个点的容量转化为上界(-)下界,并统计每个点的多余流量,新建源汇点(SS,TT)连那些多余的流量,然后跑最大流即可。如果那些源汇边上不满流,就没有可行流,否则在流量上加上该边的下界即可。

    备注:

    注意:

    1. 一个与意识上理解起来不同的地方,如果入流(>)出流,应该是(SS)(i)连边,否则是(i)(TT)连边。
    2. 跑完最大流后的图上,正边是残量网络,反边才是跑了的流量,用反边(+)下界才是答案(其实用上界(-)正边也对)。

    代码:

    #include<cstdio>
    #include<cstring>
    #include<queue>
    using namespace std;
    
    const int maxn=210,maxm=31000,inf=0x7fffffff;
    int n,m,SS,TT,tot,head[maxn],dis[maxn],cur[maxn],d[maxn];
    struct node
    {
    	int nxt,to,w,rg;
    }edge[maxm];
    queue<int>q;
    
    int read()
    {
    	int x=0;
    	char c=getchar();
    	while (c<48||c>57)
    		c=getchar();
    	while (c>=48&&c<=57)
    		x=(x<<1)+(x<<3)+(c^48),c=getchar();
    	return x;
    }
    
    void add(int u,int v,int w,int rg=0)
    {
    	edge[++tot]=(node){head[u],v,w,rg};
    	head[u]=tot;
    }
    
    void link(int u,int v,int w,int rg=0)
    {
    	add(u,v,w,rg);
    	add(v,u,0,rg);
    }
    
    bool bfs()
    {
    	memset(dis,0,sizeof(dis));
    	int i,u,v;
    	q.push(SS);
    	dis[SS]=1;
    	while (!q.empty())
    	{
    		u=q.front();
    		q.pop();
    		for (i=head[u];i;i=edge[i].nxt)
    		{
    			v=edge[i].to;
    			if (!dis[v]&&edge[i].w>0)
    			{
    				dis[v]=dis[u]+1;
    				q.push(v);
    			}
    		}
    	}
    	return dis[TT];
    }
    
    int dfs(int u,int flow)
    {
    	if (u==TT)
    		return flow;
    	int res=flow,v,tmp;
    	for (int &i=cur[u];i;i=edge[i].nxt)
    	{
    		v=edge[i].to;
    		if (dis[v]==dis[u]+1&&edge[i].w>0)
    		{
    			tmp=dfs(v,min(res,edge[i].w));
    			res-=tmp;
    			edge[i].w-=tmp;
    			edge[i^1].w+=tmp;
    			if (!res)
    				break;
    		}
    	}
    	return flow-res;
    }
    
    void dinic()
    {
    	int i,tmp;
    	while (bfs())
    	{
    		for (i=1;i<=TT;i++)
    			cur[i]=head[i];
    		tmp=dfs(SS,inf);
    		if (!tmp)
    			break;
    	}
    }
    
    int main()
    {
    	int i,u,v,b,c;
    	n=read(),m=read();
    	tot=1;
    	SS=n+1,TT=SS+1;
    	for (i=1;i<=m;i++)
    	{
    		u=read(),v=read(),b=read(),c=read();
    		link(u,v,c-b,b);
    		d[v]+=b;
    		d[u]-=b;
    	}
    	for (i=1;i<=n;i++)
    		if (d[i]!=0)
    		{
    			if (d[i]<0)
    				link(i,TT,-d[i]);
    			else
    				link(SS,i,d[i]);
    		}
    	dinic();
    	int fl=0;
    	for (i=head[SS];i;i=edge[i].nxt)
    		if (edge[i].w)
    		{
    			fl=1;
    			break;
    		}
    	if (!fl)
    	for (i=head[TT];i;i=edge[i].nxt)
    		if (edge[i^1].w)
    		{
    			fl=1;
    			break;
    		}
    	if (fl)
    		puts("NO");
    	else
    	{
    		puts("YES");
    		for (i=3;i<=2*m+1;i+=2)
    			printf("%d
    ",edge[i].w+edge[i].rg);
    	}
    	return 0;
    }
    

    (LOJ116) 有源汇上下界最大流

    题意:

    问有一个有源汇的图,流量有上下界,问最大流。

    知识点:

    有源汇上下界最大流

    解法:

    先判断可行性,还是加入(SS)(TT)来判断,但是(S)(T)处的流量不平衡,所以我们让(T)(S)连一条边(INF),这样就肯定平衡了,然后跑(SS)(TT)的最大流,判断是否可行,注意这条边的流量就是可行流的大小。然后删掉这条边,再跑(S)(T)的最大流,这个最大流(+)可行流就是答案了。

    备注:

    注意是删掉那条(T-S)的边,不是删掉(T)(S)或者是(TT)(SS)的这几个点。还有一种方法是不删这条边,然后直接跑(S)(T)的最大流,因为肯定不会选(T-S)这条边,但是反边一定选(为什么我还没懂),所以可行流就加上了。或者直接二分可行流的大小,然后赋值到那条(T-S)边上也可以(不太懂怎么实现)。

    代码:

    #include<cstdio>
    #include<cstring>
    #include<queue>
    using namespace std;
    
    const int maxn=210,maxm=20500,inf=0x7fffffff;
    int n,m,S,T,SS,TT,tot,head[maxn],cur[maxn],dis[maxn],d[maxn];
    struct node
    {
    	int nxt,to,w;
    }edge[maxm];
    queue<int>q;
    
    int read()
    {
    	int x=0;
    	char c=getchar();
    	while (c<48||c>57)
    		c=getchar();
    	while (c>=48&&c<=57)
    		x=(x<<1)+(x<<3)+(c^48),c=getchar();
    	return x;
    }
    
    void add(int u,int v,int w)
    {
    	edge[++tot]=(node){head[u],v,w};
    	head[u]=tot;
    }
    
    void lnk(int u,int v,int w)
    {
    	add(u,v,w);
    	add(v,u,0);
    }
    
    bool bfs(int s,int t)
    {
    	memset(dis,0,sizeof(dis));
    	int i,u,v;
    	q.push(s);
    	dis[s]=1;
    	while (!q.empty())
    	{
    		u=q.front();
    		q.pop();
    		for (i=head[u];i;i=edge[i].nxt)
    		{
    			v=edge[i].to;
    			if (!dis[v]&&edge[i].w>0)
    			{
    				dis[v]=dis[u]+1;
    				q.push(v);
    			}
    		}
    	}
    	return dis[t];
    }
    
    int dfs(int u,int flow,int t)
    {
    	if (u==t)
    		return flow;
    	int v,tmp,res=flow;
    	for (int &i=cur[u];i;i=edge[i].nxt)
    	{
    		v=edge[i].to;
    		if (dis[v]==dis[u]+1&&edge[i].w>0)
    		{
    			tmp=dfs(v,min(res,edge[i].w),t);
    			res-=tmp;
    			edge[i].w-=tmp;
    			edge[i^1].w+=tmp;
    			if (!res)
    				break;
    		}
    	}
    	return flow-res;
    }
    
    int dinic(int s,int t)
    {
    	int i,ans=0,tmp;
    	while (bfs(s,t))
    	{
    		for (i=1;i<=TT;i++)
    			cur[i]=head[i];
    		tmp=dfs(s,inf,t);
    		if (!tmp)
    			break;
    		ans+=tmp;
    	}
    	return ans;
    }
    
    int main()
    {
    	int i,u,v,b,c,maxflow=0;
    	n=read(),m=read(),S=read(),T=read();
    	SS=n+1,TT=SS+1;
    	tot=1;
    	for (i=1;i<=m;i++)
    	{
    		u=read(),v=read(),b=read(),c=read();
    		lnk(u,v,c-b);
    		d[u]+=b,d[v]-=b;
    	}
    	for (i=1;i<=n;i++)
    		if (d[i]!=0)
    		{
    			if (d[i]>0)
    				lnk(i,TT,d[i]),maxflow+=d[i];
    			else
    				lnk(SS,i,-d[i]);
    		}
    	lnk(T,S,inf);
    	maxflow-=dinic(SS,TT);
    	if (maxflow)
    	{
    		puts("please go home to sleep");
    		return 0;
    	}
    	maxflow=edge[tot].w;
    	edge[tot].w=edge[tot^1].w=0;
    	printf("%d
    ",maxflow+dinic(S,T));
    	return 0;
    }
    

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

    题意:

    问有一个有源汇的图,流量有上下界,问最小流。

    知识点:

    有源汇上下界最小流

    解法:

    前面连边同上下界最大流,考虑反边的最大减少量(=)正边的最小增加量,所以删边后可行流(-)(T)(S)的最大流即为最小流。

    备注:

    最好和(0)取个(max)再输出,因为不知道最小流会不会(<0)不合法。

    代码:

    #include<cstdio>
    #include<cstring>
    #include<queue>
    using namespace std;
    
    typedef long long ll;
    const int maxn=50010,maxm=350020;
    const ll inf=0x7fffffffffffffffll;
    int n,m,S,T,SS,TT,tot,head[maxn],cur[maxn],dis[maxn];
    ll d[maxn];
    struct node
    {
    	int nxt,to;
    	ll w;
    }edge[maxm];
    queue<int>q;
    
    ll read()
    {
    	ll x=0;
    	char c=getchar();
    	while (c<48||c>57)
    		c=getchar();
    	while (c>=48&&c<=57)
    		x=(x<<1)+(x<<3)+(c^48),c=getchar();
    	return x;
    }
    
    void add(int u,int v,ll w)
    {
    	edge[++tot]=(node){head[u],v,w};
    	head[u]=tot;
    }
    
    void lnk(int u,int v,ll w)
    {
    	add(u,v,w);
    	add(v,u,0);
    }
    
    bool bfs(int s,int t)
    {
    	memset(dis,0,sizeof(dis));
    	int i,u,v;
    	q.push(s);
    	dis[s]=1;
    	while (!q.empty())
    	{
    		u=q.front();
    		q.pop();
    		for (i=head[u];i;i=edge[i].nxt)
    		{
    			v=edge[i].to;
    			if (!dis[v]&&edge[i].w>0)
    			{
    				dis[v]=dis[u]+1;
    				q.push(v);
    			}
    		}
    	}
    	return dis[t];
    }
    
    ll dfs(int u,ll flow,int t)
    {
    	if (u==t)
    		return flow;
    	int v;
    	ll tmp,res=flow;
    	for (int &i=cur[u];i;i=edge[i].nxt)
    	{
    		v=edge[i].to;
    		if (dis[v]==dis[u]+1&&edge[i].w>0)
    		{
    			tmp=dfs(v,min(res,edge[i].w),t);
    			res-=tmp;
    			edge[i].w-=tmp;
    			edge[i^1].w+=tmp;
    			if (!res)
    				break;
    		}
    	}
    	return flow-res;
    }
    
    ll dinic(int s,int t)
    {
    	int i;
    	ll ans=0,tmp;
    	while (bfs(s,t))
    	{
    		for (i=1;i<=TT;i++)
    			cur[i]=head[i];
    		tmp=dfs(s,inf,t);
    		if (!tmp)
    			break;
    		ans+=tmp;
    	}
    	return ans;
    }
    
    int main()
    {
    	int i,u,v,b,c;
    	ll maxflow=0;
    	n=read(),m=read(),S=read(),T=read();
    	SS=n+1,TT=SS+1;
    	tot=1;
    	for (i=1;i<=m;i++)
    	{
    		u=read(),v=read(),b=read(),c=read();
    		lnk(u,v,c-b);
    		d[u]+=b,d[v]-=b;
    	}
    	for (i=1;i<=n;i++)
    		if (d[i]!=0)
    		{
    			if (d[i]>0)
    				lnk(i,TT,d[i]),maxflow+=d[i];
    			else
    				lnk(SS,i,-d[i]);
    		}
    	lnk(T,S,inf);
    	maxflow-=dinic(SS,TT);
    	if (maxflow)
    	{
    		puts("please go home to sleep");
    		return 0;
    	}
    	maxflow=edge[tot].w;
    	edge[tot].w=edge[tot^1].w=0;
    	printf("%lld
    ",max(0ll,maxflow-dinic(T,S)));
    	return 0;
    }
    
  • 相关阅读:
    【力扣 089】24. 两两交换链表中的节点
    【力扣 086】19. 删除链表的倒数第 N 个结点
    【力扣 085】2. 两数相加
    【力扣 093】92. 反转链表 II
    【力扣 090】25. K 个一组翻转链表
    【力扣 091】61. 旋转链表
    【力扣 088】23. 合并K个升序链表
    【力扣 087】21. 合并两个有序链表
    【力扣 092】82. 删除排序链表中的重复元素 II
    vim命令
  • 原文地址:https://www.cnblogs.com/Ronald-MOK1426/p/12317267.html
Copyright © 2020-2023  润新知