貌似费用流还是很容易看出来的。。一开始看m这么小是不是状压,后来发现没法压。。后来想费用流,一直觉得每个人修几辆车不确定不会搞了。。看了题解之后发现自己真傻比,倒着考虑就行了嘛,假如第i辆车是倒数第j个被修的,那它产生的总等待时间就是修车时间*j,然后根据这个把n个技术人员都拆成m个,第i个表示他修的倒数第i辆车,然后源点向车连流量1费用0的边,车向所有技术人员拆出的n*m个点连流量1费用为产生的对应总等待时间的边,n*m个点向汇连流量1费用0的边,跑费用流就行了。
#include<iostream>
#include<cstdio>
#include<memory.h>
#define maxn 2005
#define inf 9999999
using namespace std;
struct edge{
int e,f,q,c,next;
} ed[100005];
int n,m,i,j,k,ne=0,head,tail,hh,tt,get,to,nd,f,a[maxn],t[1005][1005],que[maxn*10],road[maxn],last[maxn],dij[maxn],minf[maxn],inq[maxn],dui[100005];
void add(int s,int e,int f,int q)
{
ed[++ne].e=e;ed[ne].f=f;
ed[ne].q=ed[ne].c=q;
ed[ne].next=a[s];a[s]=ne;
dui[ne]=ne+1;
ed[++ne].e=s;ed[ne].f=0;
ed[ne].q=inf;ed[ne].c=-q;
ed[ne].next=a[e];a[e]=ne;
dui[ne]=ne-1;
}
void update(int j,int del)
{
ed[j].f+=del;
if (ed[j].f) ed[j].q=ed[j].c;else ed[j].q=inf;
}
int spfa(int s,int t)
{
int i,j;
for (i=0;i<=nd;i++)
dij[i]=minf[i]=inf,last[i]=road[i]=-1,inq[i]=0;
head=tail=hh=tt=1;que[1]=s;
dij[s]=0;inq[s]=1;
while (hh<=tt)
{
get=que[head];
for (j=a[get];j;j=ed[j].next)
if (ed[j].f&&dij[get]+ed[j].q<dij[ed[j].e])
{
to=ed[j].e;
dij[to]=dij[get]+ed[j].q;
last[to]=get;road[to]=j;minf[to]=min(minf[get],ed[j].f);
if (!inq[to])
{
tail++;tt++;
if (tail>20000) tail=1;
que[tail]=to;
}
}
head++;hh++;if (head>20000) head=1;
}
return last[t]!=-1;
}
int fyl(int s,int t)
{
int ans=0,i;
while (spfa(s,t))
{
ans+=minf[t]*dij[t];f=minf[t];
for (i=t;last[i]!=-1;i=last[i])
update(road[i],-f),update(dui[road[i]],f);
}
return ans;
}
int main()
{
freopen("1070.in","r",stdin);
scanf("%d%d",&m,&n);
nd=n*(m+1)+1;
for (i=1;i<=n;i++)
for (j=1;j<=m;j++) scanf("%d",&t[i][j]);
for (i=1;i<=n;i++) add(0,i,1,0);
for (i=1;i<=m;i++)
for (j=1;j<=n;j++)
{
add(i*n+j,nd,1,0);
for (k=1;k<=n;k++) add(k,i*n+j,1,t[k][i]*j);
}
printf("%.2lf",(double)fyl(0,nd)/(double)n);
}