Description
同一时刻有N位车主带着他们的爱车来到了汽车维修中心。维修中心共有M位技术人员,不同的技术人员对不同
的车进行维修所用的时间是不同的。现在需要安排这M位技术人员所维修的车及顺序,使得顾客平均等待的时间最
小。 说明:顾客的等待时间是指从他把车送至维修中心到维修完毕所用的时间。
Input
第一行有两个m,n,表示技术人员数与顾客数。 接下来n行,每行m个整数。第i+1行第j个数表示第j位技术人
员维修第i辆车需要用的时间T。
Output
最小平均等待时间,答案精确到小数点后2位。
Sample Input
2 2
3 2
1 4
Sample Output
1.50
HINT
数据范围: (2<=M<=9,1<=N<=60), (1<=T<=1000)
对于这道题,可以设置超级源和超级汇,将技术人员拆为~,表示第i个技术人员倒数第1~n个修车,分别与超级汇连接一条权值为1,费用为0的边,再向每个客户连接一条权值为1,费用为(k为倒数第几个修车)边,最后从超级源向每个客户连一条权值为1,费用为0的边,在这张图上跑一遍费用流就可以了,下面是程序:
#include<stdio.h>
#include<string.h>
#include<iostream>
using namespace std;
const int N=700,INF=0x3f3f3f3f;
struct queue{
int l,r,a[N];
void clear(){
l=0,r=1;
}
bool empty(){
return l+1==r;
}
void push(int x){
a[r]=x;
r=(r+1)%N;
}
void pop(){
l=(l+1)%N;
}
int front(){
return a[(l+1)%N];
}
}q;
struct node{
int u,e;
}last[N];
struct edge{
int v,w,c,next;
}e[N*200];
int head[N],dis[N],s=0,t=N-1,k;
bool vis[N];
void add(int u,int v,int w,int c){
e[k]=(edge){v,w,c,head[u]};
head[u]=k++;
e[k]=(edge){u,0,-c,head[v]};
head[v]=k++;
}
int SPFA(){
q.clear();
memset(dis,0x3f,sizeof(dis));
dis[s]=0;
q.push(s);
vis[s]=1;
int u,i;
while(!q.empty()){
u=q.front();
q.pop();
vis[u]=0;
for(i=head[u];i!=-1;i=e[i].next){
if(e[i].w&&dis[u]+e[i].c<dis[e[i].v]){
last[e[i].v]=(node){u,i};
dis[e[i].v]=dis[u]+e[i].c;
if(!vis[e[i].v]){
vis[e[i].v]=1;
q.push(e[i].v);
}
}
}
}
return dis[t];
}
int work(){
int i,f=INF;
for(i=t;i;i=last[i].u){
f=min(f,e[last[i].e].w);
}
for(i=t;i;i=last[i].u){
e[last[i].e].w-=f;
e[last[i].e^1].w+=f;
}
return f;
}
int ek(){
int c,ans=0;
while((c=SPFA())!=INF){
ans+=c*work();
}
return ans;
}
int main(){
int n,m,i,j,k,x;
scanf("%d%d",&m,&n);
memset(head,-1,sizeof(head));
t=n*m+n+1;
for(i=1;i<=n;i++){
add(s,i,1,0);
}
for(i=1;i<=n;i++){
for(j=1;j<=m;j++){
scanf("%d",&x);
for(k=1;k<=n;k++){
add(i,j*n+k,1,k*x);
}
}
}
for(i=n+1;i<=m*n+n;i++){
add(i,t,1,0);
}
printf("%.2lf\n",ek()*1.0/n);
return 0;
}