1383. [SCOI 2007] 修车
同一时刻有N位车主带着他们的爱车来到了汽车维修中心。维修中心共有M位技术人员,不同的技术人员对不同的车进行维修所用的时间是不同的。现在需要安排这M位技术人员所维修的车及顺序,使得顾客平均等待的时间最小。
说明:顾客的等待时间是指从他把车送至维修中心到维修完毕所用的时间。
输入
第一行有两个数M,N,表示技术人员数与顾客数。
接下来n行,每行m个整数。第i+1行第j个数表示第j位技术人员维修第i辆车需要用的时间T。
输出
最小平均等待时间,答案精确到小数点后2位。
样例
repair.in
2 2
3 2
1 4
repair.out
1.50
数据范围:
(2<=M<=9,1<=N<=60), (1<=T<=1000)
思路:建立一个超级源点和超级汇点,然后在这两个点之间在建立两排点,第一排为顾客数,从源点相这一排点各引一条容量为1,费用为0的边;然后再建立一排点,非别表示第一个技术人员修1辆车,2辆车......第m个技术人员修n辆车。然后在它们中间连一条容量为1,费用为修这辆车的费用乘上这是需等待的人数(也就是修的第几个车--》其实修的第几辆车是按倒序排列的,修倒数第一辆车时,只需要一个人等,而修倒数第二辆车时,则需要两个人等);再从第二排点向汇点各连一条容量为1,流量为0的边,做一次最小费用流即可。
其实这个题也可以建立三排点,也就是将第二排拆成两排点,分别表示技术人员数和需要修的车的数目。虽然在这个题中两种方法都行,但是在一道和他很类似的题美食节中只能用三排的方法。
#include<iostream>
#include<cstdio>
#include<cstring>
#define M 210000000
using namespace std;
int map[610][610][3]={0},pre[610],l[100000]={0},h,t,n,m,dis[610];
bool f[610];
inline int SPFA(int x,int y){
memset(f,1,sizeof(f));
memset(dis,127,sizeof(dis));
memset(pre,0,sizeof(pre));
int i,j,u;
pre[x]=x;h=t=1;l[h]=x;dis[x]=0;
while(h<=t){
u=l[h];f[u]=true;
for(i=1;i<=y;++i){
if(map[u][i][1]>0&&u!=i&&dis[i]>dis[u]+map[u][i][2]){
dis[i]=dis[u]+map[u][i][2];
pre[i]=u;
if(f[i]){
f[i]=false;
t+=1;
l[t]=i;
}
}
}
h+=1;
}
return dis[y]>M?0:dis[y];
}
inline int ISAP(int x,int y){
int minn=y,i;
for(i=y;i!=x;i=pre[i]){
minn=min(minn,map[pre[i]][i][1]);
}
for(i=y;i!=x;i=pre[i]){
map[pre[i]][i][1]-=minn;
map[i][pre[i]][1]+=minn;
}
return minn;
}
int main()
{
freopen("scoi2007_repair.in","r",stdin);
freopen("scoi2007_repair.out","w",stdout);
int i,j,x,k,minn=1,ans=0;
float out;
scanf("%d%d",&m,&n);
for(i=1;i<=n;++i){
for(j=1;j<=m;++j){
scanf("%d",&x);
for(k=1;k<=n;++k){
map[i+1][1+n+(j-1)*n+k][2]=k*x;
map[1+n+(j-1)*n+k][i+1][2]=-k*x;
map[i+1][1+n+(j-1)*n+k][1]=1;
map[1+n+(j-1)*n+k][n*(m+1)+2][1]=1;
}
}
map[1][i+1][1]=1;
}
while(minn){
minn=SPFA(1,n*(m+1)+2);
if(minn) ans+=minn*ISAP(1,n*(m+1)+2);
}
out=(float)ans/n;
printf("%.2f\n",out);
}