对于这类题我们往往进行拆点处理,不过这道题比较麻烦的是一个人修不同车花费的时间不同,这就无法得到第i个人修第j次时对答案的贡献,因为我们不知道这个人一共修几辆车,也就无法得到他修第j次时,后面的车一共等了多久。所谓正难则反,我们考虑将点拆成表示第i个人修倒数第j辆车,这样我们就知道他修这辆车时,总等待时间为j*a,a是他修这辆车的时间。这样我们就可以通过这样的拆点方式来建立最小费用最大流模型,从而得到答案。
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define maxn 1005
#define maxm 100005
#define INF 1000000000
int pre[maxm],other[maxm],last[maxn],cost[maxm],cap[maxm];
int q[maxm+5],d[maxn],next[maxn],n,m,sum;
bool vis[maxn];
int S,T,l;
void connect(int x,int y,int z,int c)
{
pre[l]=last[x];
last[x]=l;
other[l]=y;
cap[l]=z;
cost[l]=c;
l++;
swap(x,y);
pre[l]=last[x];
last[x]=l;
other[l]=y;
cap[l]=0;
cost[l]=-c;
l++;
}
bool spfa()
{
memset(d,127,sizeof d);
memset(next,-1,sizeof next);
q[1]=S;d[S]=0;
int h=0,t=1;
while (h!=t)
{
h=h%maxm+1;
int u=q[h];vis[u]=0;
for (int p=last[u];p!=-1;p=pre[p])
{
if (cap[p]<=0) continue;
int v=other[p];
if (d[v]>d[u]+cost[p])
{
next[v]=p;
d[v]=d[u]+cost[p];
if (!vis[v])
{
t=t%maxm+1;
q[t]=v;
vis[v]=1;
}
}
}
}
return d[T]<INF;
}
int MCMF(void)
{
int M=0,flow=INF;
for (int p=next[T];p!=-1;p=next[other[p^1]])
flow=min(flow,cap[p]);
for (int p=next[T];p!=-1;p=next[other[p^1]])
{
cap[p]-=flow;
cap[p^1]+=flow;
M+=cost[p];
}
return M;
}
int main()
{
scanf("%d%d",&m,&n);
S=0;T=n*m+n+1;
memset(last,-1,sizeof last);
for (int i=1;i<=n;i++)
{
connect(m*n+i,T,1,0);
for (int j=1;j<=m;j++)
{
int a;
scanf("%d",&a);
for (int k=1;k<=n;k++)
connect((j-1)*n+k,i+m*n,1,a*k);
}
}
for (int i=1;i<=m;i++)
for (int k=1;k<=n;k++) connect(S,(i-1)*n+k,1,0);
while (spfa())
sum+=MCMF();
double ans=(double)sum/n;
printf("%.2lf\n",ans);
return 0;
}