zoukankan      html  css  js  c++  java
  • 网络流模板与经典模型

    网络流模板与经典模型

    1.模板

    dinic模板(常规的最大流模板,算法效率能满足大部分题)
    #include<iostream>
    #include<queue>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    #define ll long long
    const ll N=10007;
    const ll M=200007;
    const ll inf=1e18;
    ll n,m,S,T;
    ll h[N],e[M],f[M],ne[M],tot;
    ll d[N],cur[N];
    queue<ll>sa;
    void add(ll u,ll v,ll z){
    	e[tot]=v;
    	f[tot]=z;
    	ne[tot]=h[u];
    	h[u]=tot++;
    	e[tot]=u;
    	f[tot]=0;
    	ne[tot]=h[v];
    	h[v]=tot++;
    }
    bool bfs(){
    	memset(d,-1,sizeof(d));
    	while(!sa.empty())sa.pop();
    	sa.push(S);
    	d[S]=0;
    	cur[S]=h[S];
    	while(!sa.empty()){
    		ll p=sa.front();
    		sa.pop();
    		for(int i=h[p];~i;i=ne[i]){
    			ll to=e[i];
    			if(d[to]==-1&&f[i]){
    				d[to]=d[p]+1;
    				cur[to]=h[to];
    				if(to==T){
    					return true;
    				}
    				sa.push(to);
    			}
    		}
    	}
    	return false;
    }
    ll dfs(ll p,ll now){
    	if(p==T)return now;
    	ll sum=0;
    	for(int i=cur[p];~i&&sum<now;i=ne[i]){
    		cur[p]=i;
    		ll to=e[i];
    		if(d[to]==d[p]+1&&f[i]){
    			ll lin=dfs(to,min(f[i],now-sum));
    			if(lin==0){
    				d[to]=-1;
    			}
    			else{
    				f[i]-=lin;
    				f[i^1]+=lin;
    				sum+=lin;
    			}
    		}
    	}
    	return sum;
    }
    ll dinic(){
    	ll sum=0;
    	while(bfs()){
    		sum+=dfs(S,inf);
    	}
    	return sum;
    }
    int main(){
        scanf("%lld%lld%lld%lld", &n, &m, &S, &T);
        memset(h, -1, sizeof h);
        while(m--){
            ll u, v, z;
            scanf("%lld%lld%lld",&u,&v,&z);
            add(u,v,z);
        }
        printf("%lld
    ", dinic());
        return 0;
    }
    
    ISAP模板(比dinic更快,写题超时不妨试试这种)
    #include<iostream>
    #include<cstring>
    using namespace std;
    const int maxn=100050;
    const int maxm=200050;
    const int inf=1e9;
    struct edge {
        int to,w,next;
    }e[maxm<<2];
    int n,m;
    int head[maxn],cnt=1,gap[maxn],last[maxn],d[maxn],que[maxn],ql,qr;
    void init(){
      cnt=1;
      memset(head,0,sizeof(head));
    }
    void add(int u,int to,int w) {
        e[++cnt].to=to;
        e[cnt].w=w;
        e[cnt].next=head[u];
        head[u]=cnt;
        e[++cnt].to=u;
        e[cnt].w=0;
        e[cnt].next=head[to];
        head[to]=cnt;
    }
    int aug(int x,int s,int t,int mi) {
        if(x==t)return mi;
        int flow=0;
        for(int &i=last[x],v=e[i].to;i;i=e[i].next,v=e[i].to){
          if(d[x]==d[v]+1){
            int tmp=aug(v,s,t,min(mi,e[i].w));
            flow+=tmp;
            mi-=tmp;
            e[i].w-=tmp;
            e[i^1].w+=tmp;
            if(!mi)return flow;
          }
        }
        if(!(--gap[d[x]]))d[s]=t+1;
        ++gap[++d[x]],last[x]=head[x];
        return flow;
    }
    int dinic(int s,int t){
        fill(gap,gap+t+1,0);
        fill(d,d+t+1,0);
        ++gap[d[t]=1];
        for(int i=1;i<=t;++i)last[i]=head[i];
        que[ql=qr=1]=t;
        while (ql<=qr) {
            int x=que[ql++];
            for(int i=head[x],v=e[i].to;i;i=e[i].next,v=e[i].to){
              if(!d[v]){
                ++gap[d[v]=d[x]+1];
                que[++qr]=v;
              }
            }
        }
        int ans=aug(s,s,t,inf);
        while(d[s]<=t)ans+=aug(s,s,t,inf);
        return ans;
    }
    
    最小费最大流
    #
    include <bits/stdc++.h>
    using namespace std;
    #define ll long long
    const int N=5007;
    const ll inf=1e18;
    #define PII pair <ll,ll> 
    #define fr first
    #define sc second
    struct madoka{
    	vector<PII>ld;
    	struct edge{
    		ll v,f,c,r;
    	};
    	ll V=0,h[N],dis[N],pv[N],pe[N];
    	vector<edge>G[N];
    	inline void init(ll n){
    		for(ll i=0;i<=V;i++){
    			G[i].clear();
    		}
    		V=n;
    		ld.clear();
    	}
    	inline void add(ll u,ll v,ll f,ll c){
    		G[u].push_back({v,f,c,(ll)G[v].size()});
    		G[v].push_back({u,0,-c,(ll)G[u].size()-1});
    	}
    	PII dinic(ll s,ll t,ll Flow){
    		ll cost=0,flow=0,newflow;
    		fill(h,h+1+V,0);
    		while(Flow){
    			priority_queue<PII,vector<PII>,greater<PII> >Q;
    			fill(dis,dis+V+1,inf);
    			dis[s]=0,Q.push({0,s});
    			while(!Q.empty()){
    				PII now=Q.top();Q.pop();
    				ll u=now.sc;
    				if(dis[u]<now.fr) continue;
    				for(ll i=0;i<G[u].size();i++){
    					edge &e=G[u][i];
    					if(e.f>0&&dis[e.v]>dis[u]+e.c+h[u]-h[e.v]){
    						dis[e.v]=dis[u]+e.c+h[u]-h[e.v];
    						pv[e.v]=u,pe[e.v]=i;
    						Q.push(PII(dis[e.v],e.v));
    					}
    				}
    			}
    			if(dis[t]==inf) break;
    			for(ll i=0;i<=V;i++) h[i]+=dis[i];
    			newflow=Flow;
    			for(ll x=t;x!=s;x=pv[x]) newflow=min(newflow,G[pv[x]][pe[x]].f);
    			Flow-=newflow,flow+=newflow,cost+=newflow*h[t];
    			for(ll x=t;x!=s;x=pv[x]){
    				edge &e=G[pv[x]][pe[x]];
    				e.f-=newflow;
    				G[x][e.r].f+=newflow;
    			}
    		}
    		return {flow,cost};
    	}
    }A;
    int main(){
    	ll n,m;
    	while(~scanf("%lld%lld",&n,&m)){
    		ll s=1,t=n;
    		A.init(n);
    		for(ll i=1;i<=m;i++){
    			ll u,v,c;
    			scanf("%lld%lld%lld",&u,&v,&c);
    			A.add(u,v,1,c);
    		}
    		PII Ans=A.dinic(s,t,inf);
    	}
    }
    

    2.常用模型

    (1)二分图上的最大流

    飞行员配对

    简单,建超级源点与超级汇点,连上边,边权容量为1,很明显最大匹配数就是最大流。

    (2)多源汇最大流

    多源汇最大流

    简单,建超级源点与超级汇点,分别所有源点,与汇点,边权无限,跑最大流。

    (3)拆点

    餐饮

    中等,当点上有点权控制流量时,而跑普通的最大流无法满足点权的限制,此时将点视为边,即将点拆分成两点,增加一条边出来控制流量,比较考验建图能力。

    (4)最小割

    模板

    中等,一张图的最小割等于其最大流,很多题目较抽象,无法理解成求最大流,但能转换成最小割,便可通过求最大流算出最小割。、

    (5)上下界的网络

    困难,抽象难想,不好建模。

    上下界可行流

    将所有边分成两条,容量分别是下界,上界-下界。

    下界的边是必须满足的,故将下界边的出点与汇点连,入点与源点连。

    源点可以提供无限流量,让所有下界都充满水,检查汇点,若汇点流出的值小于所有下界的和,那么说明下界管子没流满水,就无可行流,反之合法。

    有源汇上下界最大流

    加汇点连一条去源点的边,便变成了无向图,按上方法求可行流,在求源汇最大流,即在满足可行流的情况下求最大流,注意去除新源汇的反边流量防止其退流,使其变成非可行流。

    有源汇上下界最小流

    和上题一样,最后只要求汇点到源点的最大流,看能退多少流回去就行。

    (6)最大权闭合子图

    最大获利

    困难,公式推导太抽象,这里只说我的理解。

    将负权点连向汇点,容量为点权*(-1)。

    将正权点连向源点,容量为点权。

    正负点权间边,容量无限。

    考虑最小割,很明显只会切源点与正点的连线,或汇点与负点的连线。

    首先,先假设正权的点我全要,此时我便要支付必须要选的负点的费用。

    而最小割,若切在负汇边上,说明我支付了该负点的费用。

    若切在正源边上,说明要获取此正点太亏,把正点权还回去实现反悔。

    所以答案就所有正点权和减去最小割。

  • 相关阅读:
    Android Studio使用
    VS.NET发送会议邮件程序原码
    C#中渐变色的代码实例,用于自绘菜单
    VS.NET获取某年某月的天数
    AJAX原理简要说明及实例
    ASP.NET下增加定时器功能
    VS.NET发送普通邮件原码
    保存xml到server实例
    VS.NET通过OUTLOOK发邮件
    利用IE打印的一点实例代码
  • 原文地址:https://www.cnblogs.com/whitelily/p/14522912.html
Copyright © 2011-2022 走看看