• 【CF708D】Incorrect Flow(费用流)


    点此看题面

    • 给定一张(n)个点(m)条边的网络,源点为(1),汇点为(n)
    • 给出每条边的容量和流量,但可能存在流量超过容量或是一个点流量不平衡的错误情况。
    • 一次操作可以将某条边的容量或流量(pm 1),求最少要多少次操作使得网络正确。
    • (n,mle 100)

    流量不平衡网络

    其实流量不平衡这种情况不仅会出现在这题,应该算是一个相当经典的问题。

    实际上,很多题目中我们都需要强制某条边流量是多少甚至强制流满,那其实就相当于是要处理一个流量不平衡的网络。

    针对这种问题的通用解法,就是记录每个点的入流减出流(d_i),如果(d_i>0)说明还没流完,向超级汇连一条容量为(d_i)的边;如果(d_i<0)说明不够流,自超级源连一条容量为(-d_i)的边。

    而对于原本的源汇点,我们还需要从原汇点向原源点连一条容量为(INF)的边(或是直接把它们看作一个点),因为它们俩放一起才需要满足流量平衡。

    增流与退流

    尽管流量的变化很灵活,但容量的变化其实是非常死板的——只有当你超过容量去增流,才需要增大容量。

    于是我们根据初始情况下容量(c)和流量(f)的关系,分类讨论建图:

    • (fle c):如果我们想给这条边增流,前(c-f)点只需(1)的代价增大流量,之后(infty)点都需要(2)的代价同时增大流量和容量;而如果我们想给这条边减流,只能减(f)点,都需要(1)的代价。
    • (f>c):首先不管我们要减少流量还是增大容量,都必须要支付(f-c)的代价,这部分可以预支掉。如果我们想给这条边增流,始终都需要(2)的代价;而如果我们想给这条边建流,前(f-c)点由于已经预支无需代价,剩余还能减(c)点都需要(1)的代价。

    这样一来再跑遍最小费用最大流,并加上预支代价即可。

    代码:(O(Dinic))

    #include<bits/stdc++.h>
    #define Tp template<typename Ty>
    #define Ts template<typename Ty,typename... Ar>
    #define Reg register
    #define RI Reg int
    #define Con const
    #define CI Con int&
    #define I inline
    #define W while
    #define N 100
    #define INF (int)1e9
    using namespace std;
    int n,m,d[N+5];
    class MinCostMaxFlow
    {
    	private:
    		#define PS (N+2)
    		#define ES (4*N)
    		#define s (n+1)
    		#define t (n+2)
    		#define E(x) ((((x)-1)^1)+1)
    		int ee,lnk[PS+5];struct edge {int to,nxt,F,C;}e[2*ES+5];
    		int lst[PS+5],IQ[PS+5],F[PS+5],C[PS+5];queue<int> q;
    		I bool SPFA()
    		{
    			RI i,k;for(i=1;i<=t;++i) F[i]=C[i]=INF;q.push(s),C[s]=0;
    			W(!q.empty()) for(i=lnk[k=q.front()],q.pop(),IQ[k]=0;i;i=e[i].nxt)
    			{
    				if(!e[i].F||C[k]+e[i].C>=C[e[i].to]) continue;
    				C[e[i].to]=C[k]+e[lst[e[i].to]=i].C,F[e[i].to]=min(F[k],e[i].F),
    				!IQ[e[i].to]&&(q.push(e[i].to),IQ[e[i].to]=1);
    			}return F[t]^INF;
    		}
    	public:
    		I void Add(CI x,CI y,CI f,CI c)
    		{
    			e[++ee].nxt=lnk[x],e[lnk[x]=ee].to=y,e[ee].F=f,e[ee].C=c,
    			e[++ee].nxt=lnk[y],e[lnk[y]=ee].to=x,e[ee].F=0,e[ee].C=-c;
    		}
    		I int MCMF()
    		{
    			RI x,g=0;W(SPFA()) {g+=C[t]*F[t],x=t;
    				W(x^s) e[lst[x]].F-=F[t],e[E(lst[x])].F+=F[t],x=e[E(lst[x])].to;}
    			return g;
    		}
    }D;
    int main()
    {
    	RI i,u,v,c,f,g=0;for(scanf("%d%d",&n,&m),i=1;i<=m;++i) scanf("%d%d%d%d",&u,&v,&c,&f),d[u]+=f,d[v]-=f,//记录度数
    		f<=c?(D.Add(u,v,c-f,1),D.Add(u,v,INF,2),D.Add(v,u,f,1)):(g+=f-c,D.Add(u,v,INF,2),D.Add(v,u,f-c,0),D.Add(v,u,c,1));//根据f与c的大小关系分类建图
    	for(i=1;i<=n;++i) d[i]>0&&(D.Add(i,t,d[i],0),0),d[i]<0&&(D.Add(s,i,-d[i],0),0);//流量不平衡,根据正负和超级源汇连边
    	return D.Add(n,1,INF,0),printf("%d
    ",g+D.MCMF()),0;//原汇点向原源点连一条容量INF的边,答案为预支代价+最小费用
    }
    
    败得义无反顾,弱得一无是处
  • 相关阅读:
    python 接口自动化测试搭建钉钉环境
    接口测试流程梳理
    兼容性测试
    软件测试流程
    软件测试---黑盒测试的测试用例的设计方法
    软件基础之-----测试的方法
    基于Selenium2+Java的UI自动化(8)- 显式等待和隐式等待
    基于Selenium2+Java的UI自动化(6)-操作Alert、confirm、prompt弹出框
    基于Selenium2+Java的UI自动化(5)
    基于Selenium2+Java的UI自动化(4)
  • 原文地址:https://www.cnblogs.com/chenxiaoran666/p/CF708D.html
Copyright © 2020-2023  润新知