zoukankan      html  css  js  c++  java
  • 上下界网络流 & 模板(LuoguP4553 80人环游世界)

    题面

    传送门
    题意简化:
    给定一个带权DAG,总共m个人,每个点正好要有Vi个人经过,求人的总花销最小(每个人分开计算花销)
    m<=79, n<=100

    Solution

    是上下界网络流的模板题:
    对于每个点流量的限制Vi,常规思路:拆点将点限制转为边限制
    所以就成了上下界网络流跑费用流,但是此处上界下界相等

    讲讲上下界网络流如何建边:

    1.对于原图中一条流量限制在[l,r],连接(u,v)的边,按如下方式建边:

    (S,v,l),(u,T,l),(u,v,r-l)

    运用补流的思想
    个人理解: 通过直接与源汇点相连,达到跳过该边前 (l) 流量的限制

    2.若原图中有源点(S)与汇点(T):

    则新增附加源汇点SS,TT;
    连边(T,S,inf), 与(SS,S,inf), (T,TT,inf)
    将SS,TT作为新的源汇点即可

    这样处理主要是为了满足每个点流量守恒的限制

    然后直接最大流即可

    code

    #include<bits/stdc++.h>
    using namespace std;
    #define re register
    #define ll long long
    #define in inline
    #define get getchar()
    in int read()
    {
        int t=0,x=1; char ch=get;
        while(ch!='-' && (ch<'0' || ch>'9')) ch=get;
        if(ch=='-') ch=get, x=-1;
        while(ch<='9' && ch>='0') t=t*10+ch-'0', ch=get;
        return t*x;
    }
    const int _=310;
    const int inf=0x3f3f3f3f;
    struct edge{
        int to,ne,w,val;
    }e[_*_<<3];
    int h[_], tot=1, n, S, T,TT,m,SS;
    int dis[_], cur[_], vis[_];
    in int bfs()
    {
        memset(vis,0,sizeof(vis));
        memset(dis,0x3f,sizeof(dis));
        queue<int >q;
        q.push(SS); dis[SS]=0;
        while(!q.empty())
        {
            int u=q.front(); q.pop();
            cur[u]=h[u];vis[u]=0;
            for(re int i=h[u];i;i=e[i].ne)
            {
                int v=e[i].to;
                if(dis[v]>dis[u]+e[i].val && e[i].w)
                {
                    dis[v]=dis[u]+e[i].val;
                    if(!vis[v]){ vis[v]=1; q.push(v);}
                }
            }
        }
        return dis[TT]!=inf;
    }
    ll cost=0;
    in int dfs(int u,int flow)
    {
        if(u==TT || !flow) return flow;
        int used=0, d; vis[u]=1;
        for(re int i=cur[u];i;i=e[i].ne)
        {
            int v=e[i].to; cur[u]=i;
            if(!vis[v] && e[i].w && dis[v]==dis[u]+e[i].val)
            {
                d=dfs(v,min(flow-used,e[i].w));
                if(!d) continue;
                used+=d, e[i].w-=d, e[i^1].w+=d, cost+=1ll*d*e[i].val;
                if(used==flow) break;
            }
        }
        return used;
    }
    int maxflow;
    in void dinic()
    {
        int qwe;
        while(bfs())
            while(memset(vis,0,sizeof(vis)), qwe=dfs(SS,inf)){ maxflow+=qwe;}
    }
    int v[_],d[_];
    in void add_edge(int x,int y,int w,int val) // 在新图上的边
    {
        e[++tot].ne=h[x], e[tot].to=y, e[tot].w=w, e[tot].val=val, h[x]=tot;
        e[++tot].ne=h[y], e[tot].to=x, e[tot].w=0, e[tot].val=-val, h[y]=tot;
    }
    in void add(int x,int y,int lf,int rf,int val) //加入有上下界的原图边
    {
        if(rf-lf>0) add_edge(x,y,rf==inf ? inf : rf-lf,val);
        if(lf) add_edge(x,TT,lf,0), add_edge(SS,y,lf,0);
    }
    int main()
    {
        n=read(), m=read();
        S=n*2+1, T=S+1, SS=T+1, TT=SS+1;
        int kt=TT+1;
        for(re int i=1;i<=n;++i) {
            v[i]=read();
            add(i,i+n,v[i],v[i],0);
            add(S,i,0,m,0), add(i+n,T,0,m,0);
        }
        for(re int i=1;i<n;++i)
        {
            for(re int j=1;j+i<=n;++j)
            {
                int x=read();
                if(x==-1) continue;
                add_edge(i+n,i+j,inf,x);
            }
        }
        add_edge(T,kt,m,0);
        add_edge(kt,S,inf,0);
        dinic();
        cout<<cost<<endl;
    }
    
    
    嗯,就这样了...
  • 相关阅读:
    android 运行时异常捕获
    汇编32位寄存器和地址编号的五种书写形式
    各种进制的乘法表,八进制的加法,和数字的源码你,反码,和补码
    第一个c程序和vs2017 在打开MFC rc文件时找不到rcdll.dl
    asdfasdf
    php如何判断一个字符串是否包含另一个字符串
    php计算时间差/两个时间日期相隔的天数,时,分,秒.
    PHP服务器时间差8小时解决方案
    历年学生作品评论
    第一周例行报告
  • 原文地址:https://www.cnblogs.com/yzhx/p/14617148.html
Copyright © 2011-2022 走看看