zoukankan      html  css  js  c++  java
  • [网络流24题(5/24)] 分配问题(最小费用最大流)

    传送门

    分析:

    非常经典的费用流的模型吧,也可以通过二分图最大匹配去做,但是鉴于二分图最大匹配的算法存在一定的局限性,故还是学一学较为通用的费用流的做法。

    这道题目中本质上要讨论的问题跟运输问题,运输问题是一致的。

    因为考虑到每个人只能被分配到一种货物,每种货物只能被一个人所分配,因此,我们不妨用流量将他们限流。

    我们创建一个超级源地(sp),将(sp)跟每个人连一条流量为(1),费用为(0)的边。

    同时我们创建一个超级汇点(ep),将每一种货物跟(ep)都连一条流量为(1),费用为(0)的边。

    同时,对于每一个人和货物,我们对他们连一条流量为无穷的边。

    因为每个人只能从超级源点获取最多(1)点的流量,每种货物只能向超级汇点传送最多(1)点的流量,因此当这个图满流时,能够保证每个人一定会配对最多一个货物,即达到我们限流的要求。

    而如果我们需要求解最小花费,我们只需要将人和货物的边加上的费用取(val_{ij}),最后在这张图上跑最小费用最大流后最小费用即为答案。

    而如果我们需要求解最大花费,我们只需要将人和货物的边加上的费用取相反数(-val_{ij}),最后在这张图上跑最小费用最大流后最小费用的相反数即为答案。

    代码:

    #include <bits/stdc++.h>
    #define maxn 4050
    using namespace std;
    struct Node{
        int to,next,val,cost;
    }q[maxn<<1];
    int head[maxn],cnt=0;
    int dis[maxn],vis[maxn],sp,ep,maxflow,cost;
    int n,num[maxn][maxn];
    const int INF=0x3f3f3f3f;
    void init(){
        memset(head,-1,sizeof(head));
        cnt=2;
        maxflow=cost=0;
    }
    void addedge(int from,int to,int val,int cost){
        q[cnt].to=to;
        q[cnt].next=head[from];
        q[cnt].val=val;
        q[cnt].cost=cost;
        head[from]=cnt++;
    }
    void add_edge(int from,int to,int val,int cost){
        addedge(from,to,val,cost);
        addedge(to,from,0,-cost);
    }
    bool spfa(){
        memset(vis,0,sizeof(vis));
        memset(dis,0x3f,sizeof(dis));
        dis[sp]=0;
        vis[sp]=1;
        queue<int>que;
        que.push(sp);
        while(!que.empty()){
            int x=que.front();
            que.pop();
            vis[x]=0;
            for(int i=head[x];i!=-1;i=q[i].next){
                int to=q[i].to;
                if(dis[to]>dis[x]+q[i].cost&&q[i].val){
                    dis[to]=dis[x]+q[i].cost;
                    if(!vis[to]){
                        que.push(to);
                        vis[to]=1;
                    }
                }
            }
        }
        return dis[ep]!=0x3f3f3f3f;
    }
    int dfs(int x,int flow){
        if(x==ep){
            vis[ep]=1;
            maxflow+=flow;
            return flow;
        }//可以到达t,加流
        int used=0;//该条路径可用流量
        vis[x]=1;
        for(int i=head[x];i!=-1;i=q[i].next){
            int to=q[i].to;
            if((vis[to]==0||to==ep)&&q[i].val!=0&&dis[to]==dis[x]+q[i].cost){
                int minflow=dfs(to,min(flow-used,q[i].val));
                if(minflow!=0){
                    cost+=q[i].cost*minflow;
                    q[i].val-=minflow;
                    q[i^1].val+=minflow;
                    used+=minflow;
                }
                //可以到达t,加费用,扣流量
                if(used==flow)break;
            }
        }
        return used;
    }int mincostmaxflow(){
        while(spfa()){
            vis[ep]=1;
            while(vis[ep]){
                memset(vis,0,sizeof(vis));
                dfs(sp,INF);
            }
        }
        return maxflow;
    }
    int main()
    {
        scanf("%d",&n);
        init();
        sp=2*n+1,ep=2*n+2;
    
        for(int i=1;i<=n;i++){
            add_edge(sp,i,1,0);
            add_edge(i+n,ep,1,0);
        }
        for(int i=1;i<=n;i++){
            for(int j=1;j<=n;j++){
                scanf("%d",&num[i][j]);
                add_edge(i,j+n,INF,num[i][j]);
            }
        }
        mincostmaxflow();
        printf("%d
    ",cost);
    
        init();
        for(int i=1;i<=n;i++){
            add_edge(sp,i,1,0);
            add_edge(i+n,ep,1,0);
        }
        for(int i=1;i<=n;i++){
            for(int j=1;j<=n;j++){
                add_edge(i,j+n,INF,-num[i][j]);
            }
        }
        mincostmaxflow();
        printf("%d
    ",-cost);
        return 0;
    }
    
  • 相关阅读:
    用脚本实现对pm2.5 数据的获取
    虚拟机下的快盘空间分配方案
    C++中new的用法
    只在堆或栈上生成对象
    Virtualbox识别USB设备
    最长公共字序列
    gdb 打印vector 等stl数据
    LC_CTYPE: cannot change locale
    字符串压缩
    对象的概念
  • 原文地址:https://www.cnblogs.com/Chen-Jr/p/11379351.html
Copyright © 2011-2022 走看看