zoukankan      html  css  js  c++  java
  • 【BZOJ2055】80人环游世界(有源汇有上下界最小费用可行流)

    点此看题面

    大致题意:(n)座城市和(m)个人,要求第(i)座城市有恰好(a_i)个人经过。每个人可以从任意城市出发,但从一座城市只能通向编号更大的城市,且有一定费用。求最少的总费用。

    前言

    果然是太久没写网络流,这么套路、这么模板的题目,居然搞了半天。。。

    有源汇有上下界可行流

    相信大家都知道什么是无源汇有上下界可行流。(不知道可以看看这篇博客【LOJ115】无源汇有上下界可行流,然后顺便看看这篇博客【LOJ116】有源汇有上下界最大流,同样对解决此题有帮助)

    对于这题,显然我们会套路地想到把一个点拆成两个点,那么就需要从入点向出点连一条上下界皆为(a_i)的边。

    根据无源汇有上下界网络流的一般套路,我们把这条边拆掉,改为从超级源向出点连一条流量(a_i)的边从入点向超级汇连一条流量(a_i)的边,然后在入点和出点之间连一条流量(0)的边(显然流量(0)的边没用,直接不管它)。

    然后由于这是有源汇的,我们还需要从源点向每个入点连一条流量(m)的边从每个出点向汇点连一条流量(m)的边。并且要注意,此时需要从汇点向源点连一条流量(m)的边,以保证只有(m)个人的限制。

    以上的边费用都是(0)

    至于点与点之间,套路地从出点向入点连边即可,具体实现详见代码。

    代码

    #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 M 10000
    #define INF (int)1e9
    using namespace std;
    int n,m;
    class MinCostPossibleFlow//有源汇有上下界最小费用可行流(其实是最小费用最大流模板)
    {
    	private:
    		#define E(x) ((((x)-1)^1)+1)
    		int ee,lnk[2*N+10];struct edge {int to,nxt,F,C;}e[2*M+5];
    		int lst[2*N+10],IQ[2*N+10],F[2*N+10],C[2*N+10];queue<int> q;
    		I bool SPFA()//SPFA求增广路
    		{
    			RI i,k;for(i=1;i<=2*n+4;++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;
    		}
    		int S,T,s,t;I void MCPF()//普通的最小费用最大流
    		{
    			RI x,res=0;W(SPFA())
    			{
    				res+=F[T]*C[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;
    			}printf("%d",res);
    		}
    }F;
    int main()
    {
    	RI i,j,x;scanf("%d%d",&n,&m),F.S=2*n+1,F.T=2*n+2,F.Add(F.t=2*n+4,F.s=2*n+3,m,0);//从汇点向源点连边
    	for(i=1;i<=n;++i) scanf("%d",&x),F.Add(F.s,i,m,0),//和超级源汇(S,T)、原图源汇(s,t)连边,不再赘述
    		F.Add(i,F.T,x,0),F.Add(F.S,n+i,x,0),F.Add(n+i,F.t,m,0);
    	for(i=1;i<=n;++i) for(j=1;j<=n-i;++j) scanf("%d",&x),~x&&(F.Add(n+i,i+j,m,x),0);//点与点之间连边
    	return F.MCPF(),0;
    }
    
  • 相关阅读:
    sqlzoo练习答案--SUM and COUNT
    响应式的嵌入内容和图片
    缓存server设计与实现(五)
    编译器DIY——读文件
    [Leetcode]-Pascal&#39;s Triangle
    zoj 1562 反素数 附上个人对反素数性质的证明
    [POJ 1236][IOI 1996]Network of Schools
    POJ 3691 DNA repair [AC自动机 DP]
    POJ 1625 Censored! [AC自动机 高精度]
    51Nod 1225 余数之和 [整除分块]
  • 原文地址:https://www.cnblogs.com/chenxiaoran666/p/BZOJ2055.html
Copyright © 2011-2022 走看看