zoukankan      html  css  js  c++  java
  • 网络流24题——数字梯形问题 luogu 4013

    题目描述:这里

    极其裸的一道费用流问题

    首先分析第一问,由于要求一个点只能经过一次,所以我们将梯形中的每一个点拆成两个点(记为入点和出点,顾名思义,入点用来承接上一行向这一行的边,出点用来向下一行连边)

    然后将出入点之间的流量设为1,边权设为0,这样就有效保证了一个点只经过一次

    接下来,我们从上一行的出点向下一行的入点连边,容量为1,费用为下一行对应点的代价的相反数,然后建起超级源点与超级终点,分别向第一行的入点连边,容量1费用0,由最后一行出点向终点连边,容量1费用0,跑一遍费用流即可(就是套路的最大费用流)

    然后看第二问,发现点可以重复经过,这样就不用拆点了,但边不能重复走,所以我们不拆点,剩下的建边与上面相同

    但是注意:两条路径可以相交于最后一行,这样的话如果最后一行的点向汇点连边的容量为1是不够的,所以设的容量要大于等于2

    第三问就是把除了源点到第一行的边以外的边边权全改为正无穷即可

    #include <cstdio>
    #include <cmath>
    #include <cstring>
    #include <cstdlib>
    #include <iostream>
    #include <algorithm>
    #include <queue>
    #include <stack>
    #define ll long long
    using namespace std;
    const ll inf=0x3f3f3f3f3f3f3f3fll;
    struct Edge
    {
        int next;
        int to;
        ll val;
        ll pri;
    }edge[10005];
    int head[1005];
    int nnum[55][55];
    ll a[55][55];
    ll dis[1005];
    int pre[1005];
    int fa[1005];
    ll lim[1005];
    bool used[1005];
    int cnt=1;
    int tot=0;
    int st,ed;
    int n,m;
    void init()
    {
        memset(edge,0,sizeof(edge));
        memset(head,-1,sizeof(head));
        cnt=1;
    }
    void add(int l,int r,ll w,ll v)
    {
        edge[cnt].next=head[l];
        edge[cnt].to=r;
        edge[cnt].val=w;
        edge[cnt].pri=v;
        head[l]=cnt++;
    }
    int ide(int x)
    {
        return (x&1)?x+1:x-1;
    }
    bool spfa()
    {
        memset(dis,0x3f,sizeof(dis));
        memset(lim,0,sizeof(lim));
        memset(used,0,sizeof(used));
        used[st]=1;
        lim[st]=inf;
        dis[st]=0;
        pre[ed]=-1;
        queue <int> M;
        M.push(st);
        while(!M.empty())
        {
            int u=M.front();
            M.pop();
            for(int i=head[u];i!=-1;i=edge[i].next)
            {
                int to=edge[i].to;
                if(edge[i].val&&dis[to]>dis[u]+edge[i].pri)
                {
                    dis[to]=dis[u]+edge[i].pri;
                    lim[to]=min(lim[u],edge[i].val);
                    pre[to]=i,fa[to]=u;
                    if(!used[to])used[to]=1,M.push(to);
                }
            }
            used[u]=0;
        }
        return pre[ed]!=-1;
    }
    ll EK()
    {
        ll maxw=0,minv=0;
        while(spfa())
        {
            maxw+=lim[ed];
            minv+=dis[ed]*lim[ed];
            int temp=ed;
            while(temp!=st)
            {
                edge[pre[temp]].val-=lim[ed];
                edge[ide(pre[temp])].val+=lim[ed];
                temp=fa[temp];
            }
        }
        return minv;
    }
    int main()
    {
        scanf("%d%d",&m,&n);
        init();
        st=0,ed=1;
        for(int i=1;i<=n;i++)
        {
            for(int j=1;j<=m+i-1;j++)
            {
                scanf("%lld",&a[i][j]);
                nnum[i][j]=++tot;
            }
        }
        for(int i=1;i<=n;i++)
        {
            for(int j=1;j<=m+i-1;j++)
            {
                add(nnum[i][j]<<1,(nnum[i][j]<<1)|1,1,-a[i][j]);
                add((nnum[i][j]<<1)|1,nnum[i][j]<<1,0,a[i][j]);
                if(i==1)
                {
                    add(st,nnum[i][j]<<1,1,0);
                    add(nnum[i][j]<<1,st,0,0);
                }
                if(i==n)
                {
                    add((nnum[i][j]<<1)|1,ed,1,0);
                    add(ed,(nnum[i][j]<<1)|1,0,0);
                }else
                {
                    add((nnum[i][j]<<1)|1,(nnum[i+1][j]<<1),1,0);
                    add((nnum[i+1][j]<<1),(nnum[i][j]<<1)|1,0,0);
                    add((nnum[i][j]<<1)|1,(nnum[i+1][j+1]<<1),1,0);
                    add((nnum[i+1][j+1]<<1),(nnum[i][j]<<1)|1,0,0);
                }
            }
        }
        printf("%lld
    ",-EK());
        init();
        for(int i=1;i<=n;i++)
        {
            for(int j=1;j<=m+i-1;j++)
            {
                if(i==1)
                {
                    add(st,nnum[i][j]+1,1,-a[i][j]);
                    add(nnum[i][j]+1,st,0,a[i][j]);
                }
                if(i==n)
                {
                    add(nnum[i][j]+1,ed,2,0);
                    add(ed,nnum[i][j]+1,0,0);
                }else
                {
                    add(nnum[i][j]+1,nnum[i+1][j]+1,1,-a[i+1][j]);
                    add(nnum[i+1][j]+1,nnum[i][j]+1,0,a[i+1][j]);
                    add(nnum[i][j]+1,nnum[i+1][j+1]+1,1,-a[i+1][j+1]);
                    add(nnum[i+1][j+1]+1,nnum[i][j]+1,0,a[i+1][j+1]);
                }
            }
        }
        printf("%lld
    ",-EK());
        init();
        for(int i=1;i<=n;i++)
        {
            for(int j=1;j<=m+i-1;j++)
            {
                if(i==1)
                {
                    add(st,nnum[i][j]+1,1,-a[i][j]);
                    add(nnum[i][j]+1,st,0,a[i][j]);
                }
                if(i==n)
                {
                    add(nnum[i][j]+1,ed,inf,0);
                    add(ed,nnum[i][j]+1,0,0);
                }else
                {
                    add(nnum[i][j]+1,nnum[i+1][j]+1,inf,-a[i+1][j]);
                    add(nnum[i+1][j]+1,nnum[i][j]+1,0,a[i+1][j]);
                    add(nnum[i][j]+1,nnum[i+1][j+1]+1,inf,-a[i+1][j+1]);
                    add(nnum[i+1][j+1]+1,nnum[i][j]+1,0,a[i+1][j+1]);
                }
            }
        }
        printf("%lld
    ",-EK());
        return 0;
    }
  • 相关阅读:
    我经历的IT公司面试及离职感受(转)
    一个优异的经理人,碰到糟糕的企业,最后往往存在的还是那间糟糕的企业(转)
    Android TextView和EditText属性详解
    Android EditText控件行尾为表情时的BUG
    Android安全问题 抢先拦截短信
    Android安全问题 抢先开机启动
    Android安全问题 抢先接收广播
    Android安全问题 抢先接收广播
    Android 监听EditView中的文本改变事件
    Android EditText中插入图片并响应点击事件
  • 原文地址:https://www.cnblogs.com/zhangleo/p/10772737.html
Copyright © 2011-2022 走看看