分配问题
题目描述
有 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 1≤n≤50,0≤ci,j≤100。
一个人只能修一个工件。
思路
我们之前的二分图是求匹配数量,但本道题求的是匹配的最优解(因为有边权了)。
而且我们之前知道:二分图的建图是把所有的容量设为 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;
}