zoukankan      html  css  js  c++  java
  • 网络流24题——负载平衡问题

    负载平衡问题:

    现有n个仓库,初始状态下每个仓库中有一些货物,且各仓库中数量不等。仓库只能向相邻的仓库搬运货物。现在想要让各个仓库的货物都相等,请求出最小的搬运量。输入n,初始状况下每个仓库的货物量,输出最少搬运量(输入保证初始货物量的平均数为整数)。

    这题有许多解法,比如说贪心。不过它是网络流24题,网络流也可以解。

    这道题要求一个最小的值,想到费用流。题解里有许多很玄妙的建图方法,不过最暴力的建图莫过于:

    设初始时仓库i中货物量为starti,它们的平均数为aver。每个仓库建一个点,另外建立源点S和汇点T。对于仓库i,首先从S连接一条容量为starti,费用为0的边,表示初始时这个仓库有这么多货物可以调用;再向T连接一条容量为aver,费用为0的边,表示最后这个仓库应该有这么多货物。这些都不算作搬运,因此费用都为0

    接下来处理仓库间的边。由于我们是暴力做法,因此按题意,每个仓库向相邻仓库各建一条容量为+∞,费用为1的边,表示这个仓库可以向两侧输送任意的货物,但这种搬运计入总代价,所以费用为1。写代码的时候可以把+∞改为货物总量。

    由于最大流已经确定为货物总量(因为连向T的边只有n条容量为aver的边,S的出边的容量和也为货物总量),因此可以在这张图上跑费用流,最小费用就是题目的解。这种方法虽然比较直白,但是也是可以AC的,十分具有灵性~

    仓库间建图的方法可以改进一下:显然过多的搬运是不必要的(即不需要先把一个仓库搬到aver以下,再把它调整回aver),但是可能会有中间转运(比如其它仓库已经达到aver,只剩下两个仓库,一个多一个少,但它们又不相邻,显然需要经过中间仓库来转运),因此可以从每个初始值大于aver的仓库向每个初始值小于aver的仓库连边,容量依然是+∞,费用为它们之间的距离(即中间经过的转运仓库数+1)来模拟中间转运的过程。

    这样建图依然跑费用流即可。

    对于n≤100的数据来讲,两种建图没什么差别~

    #include<cstdio>
    #include<cstring>
    #define min(a,b) (a<b?a:b)
    #define MXN 100+2
    #define MXM 500+5
    int n,cost_ans,flow_ans;
    int start[MXN];
    int aver;
    int v[2*MXM],flow[2*MXM],cost[2*MXM],fst[MXN],nxt[2*MXM];
    int dis[MXN],prev[MXN],prev_edge[MXN];
    bool vis[MXN];
    int etop;
    int S,T;
    int queue[MXN*MXN],head,tail;
    void AddEdge(int x,int y,int z,int w){
        v[etop]=y;
        flow[etop]=z;
        cost[etop]=w;
        nxt[etop]=fst[x];
        fst[x]=etop++;
        v[etop]=x;
        flow[etop]=0;
        cost[etop]=-w;
        nxt[etop]=fst[y];
        fst[y]=etop++;
        return;
    }
    bool EKSPFA(){
        memset(vis,0,sizeof(vis));
        memset(dis,0x7f,sizeof(dis));
        int x,y;
        head=tail=0;
        queue[tail++]=S;
        dis[S]=0;
        vis[S]=true;
        while(head<tail){
            x=queue[head++];
            for(y=fst[x];y!=-1;y=nxt[y]){
                if(flow[y]>0 && dis[v[y]]>dis[x]+cost[y]){
                    if(vis[v[y]]==false){
                        vis[v[y]]=true;
                        queue[tail++]=v[y];
                    }
                    dis[v[y]]=dis[x]+cost[y];
                    prev[v[y]]=x;
                    prev_edge[v[y]]=y;
                }
            }
            vis[x]=false;
        }
        if(dis[T]==0x7f7f7f7f) return false;
        else return true;
    }
    void MinCostFlow(){
        cost_ans=flow_ans=0;
        int f;
        while(EKSPFA()){
            f=0x6fffffff;
            for(int i=T;i!=S;i=prev[i]){
                f=min(f,flow[prev_edge[i]]);
            }
            flow_ans+=f;
            cost_ans+=f*dis[T];
            for(int i=T;i!=S;i=prev[i]){
                flow[prev_edge[i]]-=f;
                flow[prev_edge[i]^1]+=f;
            }
        }
        return;
    }
    int Next(int x){
        if(x+1<=n) return x+1;
        else return 1;
    }
    int Prev(int x){
        if(x-1>0) return x-1;
        else return n;
    }
    int main(){
        memset(fst,-1,sizeof(fst));
        memset(nxt,-1,sizeof(nxt));
        scanf("%d",&n);
        S=0,T=n+1;
        etop=aver=0;
        for(int i=1;i<=n;i++){
            scanf("%d",&start[i]);
            AddEdge(S,i,start[i],0);
            aver+=start[i];
        }
        aver/=n;
        for(int i=1;i<=n;i++){
            AddEdge(i,T,aver,0);
            AddEdge(i,Next(i),aver*n,1);
            AddEdge(i,Prev(i),aver*n,1);
        }
        MinCostFlow();
        printf("%d
    ",cost_ans);
        return 0;
    }
    代码
  • 相关阅读:
    SCHTASKS /CREATE
    手机酷派4G5316 5313s 黑砖 求转成功 9008端口 9006端口 少走弯路选对镜像
    网络共享 相关知识与原理 操作步骤
    电脑 主板 硬盘的 电脑系统
    按键精灵 按键代码
    win7 快捷键 收集
    默认主页更改 主页锁定 打开浏览器时的网页设置
    按键精灵 以时间命名文件夹 创建文件 写入文件 和截图
    按键精灵-----按钮控制(开始子程序)的时候是要用到多线程的
    java web 大总结
  • 原文地址:https://www.cnblogs.com/halifuda/p/8386450.html
Copyright © 2011-2022 走看看