zoukankan      html  css  js  c++  java
  • P4553 80人环游世界(上下界费用流)

    P4553 80人环游世界

    emm......先从上下界网络流(转)开始

    再到现在的上下界费用流

    因为有上下界,我们需要记下每个点的流量差$ex[i]$,用于调整

    $ins(x,y,l,r,v)=link(x,y,r-l,v),ex[x]-=l,ex[y]+=l$

    每个点都得经过$w$次,等价于:

    把每个点$i$拆成$i_1,i_2$,$ins(i_1,i_2,w,w,0)$

    题目限制总流量$=m$,于是设置一个附加点$T0$,$ins(T0,T,0,m,0)$,(最终一定会流满$m$)

    对于其他点,也套路地连边:

    $ins(S,i_1,0,m,0)$

    $ins(i_2,T0,0,m,0)$

    对于两点之间的边$(i,j,w)$:$ins(i_2,j_1,0,m,w)$

    使源汇点流量守恒:$ins(T,S,0,m,0)$

     

    蓝后就是对原图的调整(原图可能有些点不流量守恒):

    按照上下界网络流的套路,新建调整源点汇点$pS,pT$

    $if(ex[i]<0) link(i,pT,-ex[i],0);$
    $if(ex[i]>0) link(pS,i,ex[i],0);$

    最后直接以$pS,pT$为源点汇点跑费用流

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<queue>
    using namespace std;
    #define N 2005
    #define M 100005
    int n,m,S,T0,T,pS,pT,ex[N],d[N],a[N],p[N],tC;
    queue <int> h; bool inh[N];
    int cnt=1,hd[N],nxt[M],ed[N],poi[M],val[M],cst[M];
    inline void adde(int x,int y,int v1,int v2){
        nxt[ed[x]]=++cnt, hd[x]=hd[x]?hd[x]:cnt,
        ed[x]=cnt, poi[cnt]=y, val[cnt]=v1,cst[cnt]=v2;
    }
    inline void link(int x,int y,int v1,int v2){adde(x,y,v1,v2),adde(y,x,0,-v2);}
    inline void ins(int x,int y,int l,int r,int v){link(x,y,r-l,v),ex[x]-=l,ex[y]+=l;}
    bool bfs(){
        memset(d,127,sizeof(d)); int inf=d[0];
        h.push(S); d[S]=0; a[S]=inf; inh[S]=1;
        while(!h.empty()){
            int x=h.front(); h.pop(); inh[x]=0;
            for(int i=hd[x];i;i=nxt[i]){
                int to=poi[i];
                if(val[i]>0&&d[to]>d[x]+cst[i]){
                    d[to]=d[x]+cst[i]; p[to]=i;
                    a[to]=min(a[x],val[i]);
                    if(!inh[to]) inh[to]=1,h.push(to);
                }
            }
        }if(d[T]==inf) return 0;
        tC+=a[T]*d[T];
        for(int i=T;i!=S;i=poi[p[i]^1])
            val[p[i]]-=a[T],val[p[i]^1]+=a[T];
        return 1;
    }
    int main(){
        scanf("%d%d",&n,&m); int w;
        S=2*n+1; T0=S+1; T=T0+1; pS=T+1; pT=pS+1;
        ins(T0,T,0,m,0);
        for(int i=1;i<=n;++i){
            ins(S,i,0,m,0);
            ins(i+n,T0,0,m,0);
            scanf("%d",&w);
            ins(i,i+n,w,w,0);
        }
        for(int i=1;i<=n;++i)
            for(int j=i+1;j<=n;++j){
                scanf("%d",&w);
                if(w>-1) ins(i+n,j,0,m,w);
            }
        for(int i=1;i<=T;++i){//调整
            if(ex[i]<0) link(i,pT,-ex[i],0);
            if(ex[i]>0) link(pS,i,ex[i],0);
        }ins(T,S,0,m,0);
        swap(S,pS);swap(T,pT);
        while(bfs());
        printf("%d",tC);
        return 0;
    }

     

  • 相关阅读:
    RHEL 6.3 详细安装教程
    如何利用sendmail发送外部邮件?
    阿里云API网关(14)流控策略
    阿里云API网关(13)请求身份识别:客户端请求签名和服务网关请求签名
    OpenID Connect 是什么?
    OpenID Connect + OAuth2.0
    OAuth是什么?
    OpendID是什么?
    【华为无线路由器】连接【广电光纤入户设备】宽带上网
    windows7.0旗舰版安装后控制面板自带的Microsoft程序
  • 原文地址:https://www.cnblogs.com/kafuuchino/p/10803659.html
Copyright © 2011-2022 走看看