zoukankan      html  css  js  c++  java
  • ACMICPC Live Archive 5095 Transportation

    最小费用最大流

    2010的国赛题,题意很易懂不解释了。

    分析部分参考了别人博客:一般的最小费用最大流是给出每个单位流量的费用,计算费用的时候是单位费用*流量,但是这里是单位费用*流量*流量,并不能单纯地计算,要巧妙地拆容量,每条边的原容量为cap,拆成cap条,每条容量都是1,第一次取这条路时是单位费用a,第二次是3*a,依次为5*a,7*a,9*a,这样你会发现,这条路走容量2的话刚好是4a,3为9a,4为16a,5为25a,接下来做一最小费用最大流即可

    另外注意一点,题中说明边数最大为5000,而每条边的容量最大为5,所以在开数组的时候应该开到 2*5*50000,原来的一条边要拆成cap条边,所以边数变为cap倍,即*5,因为邻接表建图,有向边还要保存它的反边,每条边再*2,一共是*10

    整道题就是拆边巧妙,其余部分都是最小费用最大流的模板

    #include <cstdio>
    #include <cstring>
    #include <vector>
    #include <queue>
    #include <algorithm>
    using namespace std;
    #define INF 0x3f3f3f3f
    #define MAXN 110
    #define MAXM 5010 //有向图
    
    int nume;
    struct edge
    {
        int u,v,cap,cost,flow,next;
    }e[10*MAXM];
    int first[MAXN];
    int d[MAXN];  //spfa最短路
    int p[MAXN]; //增广记录路径
    int N,M,K,S,T;
    
    void add(int u, int v ,int cost ,int cap)
    {
        e[nume].u=u; e[nume].v=v; 
        e[nume].cost=cost; e[nume].cap=cap; e[nume].flow=0;
        e[nume].next=first[u]; first[u]=nume++;
    }
    
    void build()
    {
        memset(first,-1,sizeof(first));
        nume=0;
        S=0; T=N+1;
        //限制最大流为K
        add(S,1,0,K); add(1,S,0,0);
        add(N,T,0,K); add(T,N,0,0);
        for(int i=0; i<M; i++)
        {
            int u,v,cost,cap;
            scanf("%d%d%d%d",&u,&v,&cost,&cap);
            for(int j=1; j<=cap; j++) //拆边的方法
            {//每条边的容量为1,单位费用为(2*j-1)*cost,其实就是1,3,5,7,9
                add(u,v,(2*j-1)*cost,1);
                add(v,u,-(2*j-1)*cost,0);
            }
        }
    }
    
    bool spfa()
    {
        queue<int>q;
        bool inq[MAXN];
        memset(inq,false,sizeof(inq));
        memset(d,0x3f,sizeof(d));
        memset(p,-1,sizeof(p));
        d[S]=0; q.push(S); inq[S]=true;
        while(!q.empty())
        {
            int u,v,cost,cap,flow;
            u=q.front(); q.pop(); inq[u]=false;
            for(int k=first[u]; k!=-1; k=e[k].next)
            {
                v=e[k].v; cost=e[k].cost; cap=e[k].cap; flow=e[k].flow;
                if(cap>flow && d[u]+cost < d[v])
                {
                    d[v]=d[u]+cost;
                    p[v]=k;
                    if(!inq[v])
                    {
                        q.push(v);
                        inq[v]=true;
                    }
                }
            }
        }
        if(d[T]!=INF) return true;
        return false;
    }
    
    void mincost_maxflow()
    {
        int FLOW=0,COST=0;
        while(spfa())
        {
            int min=INF;
            for(int k=p[T]; k!=-1; k=p[e[k].u])
                if(e[k].cap-e[k].flow<min) min=e[k].cap-e[k].flow;
            //printf("min=%d\n",min);
            for(int k=p[T]; k!=-1; k=p[e[k].u]) //增广
            {
                e[k].flow += min;
                e[k^1].flow -= min;
            }
            COST += d[T]*min;
            FLOW += min;
        }
        if(FLOW<K)
            printf("-1\n");
        else
            printf("%d\n",COST);
    }
    
    int main()
    {
        while(scanf("%d%d%d",&N,&M,&K)!=EOF)
        {
            build();
            mincost_maxflow();
        }
        return 0;
    }
  • 相关阅读:
    vi使用方法详细介绍
    Jenkins实现Android自动化打包
    JSON知识总结
    React Native中pointerEvent属性
    从零学React Native之06flexbox布局
    Android Http实现文件的上传和下载
    从零学React Native之05混合开发
    React Native声明属性和属性确认
    从零学React Native之04自定义对话框
    Android 在图片的指定位置添加标记
  • 原文地址:https://www.cnblogs.com/scau20110726/p/2961982.html
Copyright © 2011-2022 走看看