原题链接:https://www.lydsy.com/JudgeOnline/problem.php?id=1070
修车
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)
题解
假设一个人修了
k
k
辆车,维修这辆车所需要的时间分别为
t1,t2,⋯,tk
t
1
,
t
2
,
⋯
,
t
k
,那么顾客总共等待的时间为:
反着思考一下,倒数第 i i 个修的车对等待时间的贡献为。
那么我们不如将每个维修员拆成 n n 个点,分别对应修的倒数第个车,代表车的点向 n×m n × m 个点依次连边,容量为 1 1 ,费用为。
代码
#include<bits/stdc++.h>
using namespace std;
const int M=1e4+5;
struct sd{int to,fl,val;}ed[M*30];
int head[M],nxt[M*30],num[M*30],dis[M],minf[M],pre[M],car[65][65],n,m,s,t,id,cnt,ans,mx;
bool vis[M];
queue<int>dui;
void added(int f,int t,int w,int v){nxt[++cnt]=head[f],head[f]=cnt,num[cnt]=id;ed[id++]=(sd){t,w,v};}
void add(int f,int t,int w,int v){added(f,t,w,v),added(t,f,0,-v);}
bool SPFA(int s,int t)
{
memset(dis,127,sizeof(dis));memset(vis,0,sizeof(vis));
dui.push(s);dis[s]=0,vis[s]=1,minf[s]=0x3f3f3f3f;
int f;sd hh;
while(!dui.empty())
{
f=dui.front();dui.pop();
vis[f]=0;
for(int i=head[f];i;i=nxt[i])
{
hh=ed[num[i]];
if(hh.fl>0&&dis[hh.to]>dis[f]+hh.val)
{
dis[hh.to]=dis[f]+hh.val;
minf[hh.to]=min(minf[f],hh.fl);
pre[hh.to]=num[i];
if(!vis[hh.to])dui.push(hh.to),vis[hh.to]=1;
}
}
}
return dis[t]<2e9;
}
void up(int s,int t)
{
int v=t,hh;
while(v!=s)hh=pre[v],ed[hh].fl-=minf[t],ed[hh^1].fl+=minf[t],v=ed[hh^1].to;
ans+=minf[t]*dis[t];
}
void in()
{
int a;scanf("%d%d",&m,&n);t=(mx=n*m)+n+1;
for(int i=1;i<=n;++i)for(int j=1;j<=m;++j)scanf("%d",&car[i][j]);
for(int i=1;i<=n;++i)add(s,mx+i,1,0);for(int i=1;i<=mx;++i)add(i,t,1,0);
for(int i=1;i<=n;++i)for(int j=1;j<=m;++j)for(int k=1;k<=n;++k)add(mx+i,(j-1)*n+k,1,k*car[i][j]);
}
void ac()
{
while(SPFA(s,t))up(s,t);
printf("%.2lf\n",1.0*ans/n);
}
int main(){in();ac();}