[网络流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\n",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\n",-cost);
    return 0;
}

原文地址:https://www.cnblogs.com/Chen-Jr/p/11379351.html

时间: 2024-11-01 15:24:41

[网络流24题(5/24)] 分配问题(最小费用最大流)的相关文章

LiberOJ #6013. 「网络流 24 题」负载平衡 最小费用最大流 供应平衡问题

#6013. 「网络流 24 题」负载平衡 内存限制:256 MiB时间限制:1000 ms标准输入输出 题目类型:传统评测方式:文本比较 上传者: 匿名 提交提交记录统计讨论测试数据 题目描述 G 公司有 n nn 个沿铁路运输线环形排列的仓库,每个仓库存储的货物数量不等.如何用最少搬运量可以使 n nn 个仓库的库存数量相同.搬运货物时,只能在相邻的仓库之间搬运. 输入格式 文件的第 1 11 行中有 1 11 个正整数 n nn,表示有 n nn 个仓库.第 2 22 行中有 n nn 个

BZOJ 1221 HNOI 2001 软件开发/网络流24题 餐巾计划问题 最小费用最大流

题目大意:有一个软件公司,每天需要给一些员工准备消毒毛巾,这些毛巾可以循环利用,但是需要消毒.可以将毛巾送去消毒,有两种方式,A天fA花费,B天fB花费.或者还可以直接买新毛巾,问为了满足员工的需求,至少需要花多少钱. 思路:经典的费用流问题.将每一天拆点,S向每一天<<1连边,约束每一天需要多少毛巾:每一天<<1|1向T连边,约束每一天需要的毛巾.每一天<<1向这一天清洗的毛巾能够使用的那一天<<1|1,注意A和B.毛巾可以延后使用,那么每一天<&l

【网络流24题】餐巾计划问题(最小费用最大流)

[网络流24题]餐巾计划问题(最小费用最大流) 题面 COGS 洛谷上的数据范围更大,而且要开longlong 题解 餐巾的来源分为两种: ①新买的 ②旧的拿去洗 所以,两种情况分别建图 先考虑第一种 因为新买餐巾没有任何限制,并且随时可以买 所以直接从源点向每一天连边,容量为INF,费用为餐巾的价格 因为流要流出去,所以每个点向汇点连边,容量为每天的用量,费用为0 第二种,旧的拿去洗 首先考虑一下怎么算有多少旧的餐巾 每天用旧的餐巾的数量值一定的,不可能变多 因此从源点向这些点连边,容量为每天

CGOS461 [网络流24题] 餐巾(最小费用最大流)

题目这么说的: 一个餐厅在相继的N天里,第i天需要Ri块餐巾(i=l,2,…,N).餐厅可以从三种途径获得餐巾. 购买新的餐巾,每块需p分: 把用过的餐巾送到快洗部,洗一块需m天,费用需f分(f<p).如m=l时,第一天送到快洗部的餐巾第二天就可以使用了,送慢洗的情况也如此. 把餐巾送到慢洗部,洗一块需n天(n>m),费用需s分(s<f). 在每天结束时,餐厅必须决定多少块用过的餐巾送到快洗部,多少块送慢洗部.在每天开始时,餐厅必须决定是否购买新餐巾及多少,使洗好的和新购的餐巾之和满足当

【COGS 461】[网络流24题] 餐巾 最小费用最大流

既然是最小费用最大流我们就用最大流来限制其一定能把每天跑满,那么把每个表示天的点向T连流量为其所需餐巾,费用为0的边,然后又与每天的餐巾对于买是无限制的因此从S向每个表示天的点连流量为INF,费用为一个餐巾的费用的边,然后我们考虑怎么用旧餐巾,我们用旧餐巾,要既不影响本点流量,也不影响本点费用,因此我们在开一坨点表示其对应得那天的旧餐巾,并通过他向离他快洗和离他慢洗天数的天的点连边,流量为Inf,费用为快洗.慢洗的费用,然后对于多余的旧餐巾,我们在一排天点中间从第一天连续地连到最后一天,流量为I

「网络流24题」 18. 分配问题

「网络流24题」 18. 分配问题 <题目链接> 费用流其实是可以做这题的. 但这篇主要说一下二分图最佳完美匹配--Kuhn-Munkres(KM)算法. 工作是X部,费用是Y部,边权为工作效益. 通过X部减去/Y部增加增广路上的松弛量,修改「顶标」(又称标杆). 初始顶标:X部点:最大权出边的边权:Y部点:0. 跑出来后,所有顶标和是最大效益. 所有边取负,跑出来的和的相反数是最小效益. 具体请看此篇题解. KM写法 #include <algorithm> #include &

网络流24题 -No.18 分配问题

问题描述 有 n件工作要分配给 n个人做.第 i 个人做第 j 件工作产生的效益为c[i,j] .试设计一个将n 件工作分配给 n个人做的分配方案,使产生的总效益最大. 编程任务对于给定的 n件工作和 n 个人,计算最优分配方案和最差分配方案. 数据输入输入的第 1 行有 1 个正整数 n,表示有 n件工作要分配给 n 个人做.接下来的 n 行中,每行有 n 个整数c[i,j],1≤i≤n,1≤j≤n,表示第 i 个人做第 j 件工作产生的效益为c[i,j] . 结果输出程序运行结束时,输出最小

【Codevs1237&amp;网络流24题餐巾计划】(费用流)

题意:一个餐厅在相继的 N 天里,每天需用的餐巾数不尽相同. 假设第 i 天需要 ri块餐巾(i=1,2,-,N).餐厅可以购买新的餐巾,每块餐巾的费用为 p 分: 或者把旧餐巾送到快洗部,洗一块需 m 天,其费用为 f 分: 或者送到慢洗部,洗一块需 n 天(n>m),其费用为 s<f 分.每天结束时,餐厅必须决定将多少块脏的餐巾送到快洗部,多少块餐巾送到慢洗部,以及多少块保存起来延期送洗. 但是每天洗好的餐巾和购买的新餐巾数之和,要满足当天的需求量.试设计一个算法为餐厅合理地安排好 N 天

【网络流24题】骑士共存问题(最大流)

[网络流24题]骑士共存问题(最大流) 题面 Cogs 题解 这题本质上和方格取数问题没有任何区别 首先也是可以黑白染色 因为马必定会跳到异色点上面去 然后同样的,源点向一种颜色,另一种颜色向汇点连边 因为代价就是1,所以容量都是1 这里考虑的"相邻"的情况是马的跳法 因此,枚举从当前点能够到达的位置,连一条容量为INF的边过去 障碍直接特殊考虑就行了 最后的答案就是所有可以放的位置数减去最大流(最小割) #include<iostream> #include<cst