分配问题(二分图最优匹配问题):网络流实现:费用流写

分配问题

题目描述

n n n 件工作要分配给 n n n 个人做。第 i i i 个人做第 j j j 件工作产生的效益为 c i j c_{ij} cij 。试设计一个将 n n n 件工作分配给 n n n 个人做的分配方案,使产生的总效益最小或最大。

输入格式

文件的第一行有 1 1 1 个正整数 n n n,表示有 n n n 件工作要分配给 n n n 个人做。

接下来的 n n n 行中,每行有 n n n 个整数 c i , j c_{i,j} ci,j,表示第 i i i 个人做第 j j j 件工作产生的效益为 c i , j c_{i,j} ci,j

输出格式

两行分别输出最小总效益和最大总效益。

样例 #1

样例输入 #1

5
2 2 2 1 2
2 3 1 2 4
2 0 1 1 1
2 3 4 3 3
3 2 1 2 1

样例输出 #1

5
14

提示

1 ≤ n ≤ 50 , 0 ≤ c i , j ≤ 100 1 \leq n \leq 50, 0 \le c _ {i, j} \le 100 1n50,0ci,j100

一个人只能修一个工件。

思路

我们之前的二分图是求匹配数量,但本道题求的是匹配的最优解(因为有边权了)。

而且我们之前知道:二分图的建图是把所有的容量设为 1 1 1

拓展:二分图没有环。请添加图片描述
拓展1:费用流包括:最小费用最大流,最大费用最大流。(二者的求法一样,对于求最大费用最大流我们可以取反操作来做),对于求最小费用最大流我们的 spfa 就是求类似我们之前求最短路。

拓展2:我们还原原网络通过:

for(int i=0;i<idx;i+=2){
    f[i]+=f[i^1],f[i^1]=0;
}

代码

//二分图最优分配问题

#include<iostream>
#include<algorithm>
#include<cstring>
#include<queue>

using namespace std;

const int N = 110,M = (N*2+N*N)*2+10,INT = 1e8;

int e[M],ne[M],f[M],w[M],h[N],idx;
bool st[N];
int pre[N],d[N],incf[N];
int n,S,T;
int cost[N][N];

void add(int a,int b,int c,int d){
    e[idx]=b,f[idx]=c,w[idx]=d,ne[idx]=h[a],h[a]=idx++;
    e[idx]=a,f[idx]=0,w[idx]=-d,ne[idx]=h[b],h[b]=idx++;
}

bool spfa(){
    queue<int>q;
    q.push(S);
    
    memset(incf,0,sizeof incf);
    memset(d,0x3f,sizeof d);
    
    d[S]=0,incf[S]=INT;
    
    while(q.size()){
        int t=q.front();
        q.pop();
        st[t]=false;
        
        for(int i=h[t];~i;i=ne[i]){
            int ver=e[i];
            
            if(f[i]&&d[ver]>d[t]+w[i]){
                d[ver]=d[t]+w[i];
                pre[ver]=i;
                incf[ver]=min(f[i],incf[t]);
                if(!st[ver]){
                    st[ver]=true;
                    q.push(ver);
                }
            }
        }
    }
    return incf[T]>0;
}

int EK(){
    int cost=0;
    
    while(spfa()){
        int t=incf[T];
        cost+=t*d[T];
        
        for(int i=T;i!=S;i=e[pre[i]^1]){
            f[pre[i]]-=t;
            f[pre[i]^1]+=t;
        }
    }
    return cost;
}

int main(){
    cin>>n;
    
    S=0,T=n*2+1;
    
    memset(h,-1,sizeof h);
    
    for(int i=1;i<=n;i++){
        for(int j=1;j<=n;j++){
            cin>>cost[i][j];
        }
    }
    
    for(int i=1;i<=n;i++)add(S,i,1,0);
    for(int i=1;i<=n;i++)add(i+n,T,1,0);
    
    for(int i=1;i<=n;i++){
        for(int j=1;j<=n;j++){
            add(i,j+n,1,cost[i][j]);
        }
    }
    
    cout<<EK()<<endl;
    
    for(int i=0;i<idx;i+=2){
        f[i]+=f[i^1],f[i^1]=0;
        w[i]=-w[i],w[i^1]=-w[i^1];
    }
    
    cout<<-EK();
    
    return 0;
    
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

green qwq

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值