【网络流24题-18】分配问题
Description
【问题描述】:
有n件工作要分配给n(n<=100)个人做。第i个人做第 j件工作产生的效益为cij。试设计一个将n件工作分配给n个人做的分配方案,使产生的总效益最大。
【编程任务】:
对于给定的n件工作和 n个人,计算最优分配方案和最差分配方案。
有n件工作要分配给n(n<=100)个人做。第i个人做第 j件工作产生的效益为cij。试设计一个将n件工作分配给n个人做的分配方案,使产生的总效益最大。
【编程任务】:
对于给定的n件工作和 n个人,计算最优分配方案和最差分配方案。
Input
由文件input.txt提供输入数据。文件的第1行有1个正整数 n,表示有 n件工作要分配给 n 个人做。接下来的 n 行中,每行有 n 个整数cij,1≤i≤n,1≤j≤n,表示第 i 个人做第j件工作产生的效益为cij。
Output
程序运行结束时,将计算出的最小总效益和最大总效益输出到文件output.txt中。
Sample Input
52 2 2 1 2
2 3 1 2 4
2 0 1 1 1
2 3 4 3 3
3 2 1 2 1
Sample Output
5
14
14
Solution
给出在保证最大流的情况下费用/收益的最大/最小值,可以归纳为最小费用最大流的模型。
我们可以发先,最大流的一种求法是不断寻找从源点到汇点的路径,当没有新的增广路径时,当前的流量即为最大流量。如果我们不断优先寻找最小费用的增广路,那么最后的最大流就会对应着最小的费用,符合贪心的原理。 具体处理时,我们用最短路的几种不同算法均可,但考虑到有负权边,还要兼顾到较高效率,SPFA是一种不错的选择。本题的具体建图过程如下:
1、从 S 向每个 Xi 连一条容量为 1,费用为 0 的有向边。
2、从每个 Yi向 T 连一条容量为 1,费用为 0 的有向边。
3、从每个 Xi 向每个 Yj 连接一条容量为无穷大,费用为 C[i,j]的有向边。 求最小费用最大流,最小费用流值就是最少运费,求最大费用最大流,最大费用流值就是最多运费。
当我们求最大费用时,将原图中的费用取反,得到的答案就是最大费用的相反数。
CODE
格式丑陋的蒟蒻代码。。。
#include<iostream>
#include<cstring>
#include<cstdio>
#include<queue>
using namespace std;
const int inf=0x3f3f3f3f;
struct Pipe{int next,to,flow,cost;}pipe[20005];
Pipe tg[20005];
int h[505],cnt=1;
inline void add(int x,int y,int z,int k){
pipe[++cnt].to=y;pipe[cnt].next=h[x];h[x]=cnt;
pipe[cnt].flow=z;pipe[cnt].cost=k;
pipe[++cnt].to=x;pipe[cnt].next=h[y];h[y]=cnt;
pipe[cnt].flow=0;pipe[cnt].cost=-k;//反向边的花费与费用相反
return ;
}
inline int read(){
char c;int rec=0;
while((c=getchar())<'0'||c>'9');
while(c>='0'&&c<='9')rec=rec*10+c-'0',c=getchar();
return rec;
}
int N,n,st,ed;
int maxx,minn,ans;
int vis[505],dis[505],prep[505],pree[505];
inline bool SPFA(int st,int ed){
queue<int> q;
int i,j,p;
memset(prep,0,sizeof(prep));memset(pree,0,sizeof(pree));
memset(vis,0,sizeof(vis));memset(dis,0x3f,sizeof(dis));
dis[st]=0;q.push(st);vis[st]=1;
while(!q.empty()){
p=q.front();q.pop();vis[p]=0;
for(i=h[p];i;i=pipe[i].next){
j=pipe[i].to;
if(pipe[i].flow&&dis[j]>dis[p]+pipe[i].cost){
dis[j]=dis[p]+pipe[i].cost;
prep[j]=p;pree[j]=i;//记录最短路径的节点和边
if(!vis[j]){q.push(j);vis[j]=1;}
}
}
}
return dis[ed]<inf;
}
inline void Adjust(){
int i,j,p=inf;
for(i=ed;i!=st;i=prep[i]){
j=pree[i];
p=min(p,pipe[j].flow);
}
for(i=ed;i!=st;i=prep[i]){
j=pree[i];
pipe[j].flow-=p;pipe[j^1].flow+=p;
}
ans+=p*dis[ed];
return ;
}//根据最短路径更改网络
int main(){
N=read();st=0;ed=N*2+1;n=N*2+2;
int i,j,x;
for(i=1;i<=N;i++)add(st,i,1,0),add(i+N,ed,1,0);
for(i=1;i<=N;i++){
for(j=1;j<=N;j++){
x=read();
add(i,j+N,inf,x);
}
}
for(i=1;i<=cnt;i++)tg[i]=pipe[i],tg[i].cost=-tg[i].cost;//储存并取反
ans=0;
while(SPFA(st,ed))Adjust();
minn=ans;ans=0;
for(i=1;i<=cnt;i++)pipe[i]=tg[i];
while(SPFA(st,ed))Adjust();
maxx=-ans;
cout<<minn<<endl<<maxx;
return 0;
}