题目传送门:https://www.luogu.org/problemnew/show/P2053
题目分析:这题是我今早上数学课的时候,花10分钟突然想到的……
它要我们求平均值最小,其实就是求和最小。先简化问题:假设每一辆车车主的等待时间都等于修他自己车的时间,要怎么构图?很明显就是下面这样(虽然贪心就可以做了,但我们还是将图画出来):
其中逗号左边的数代表流量上限,右边的数代表费用。左边的一列点是车辆,右边的一列点是维修人员。
现在回到原问题:每一个车主的等待时间等于修他自己和前面的车的时间。
假设一个人修了3辆车,从左到右编号为3,5,6,记r[i]表示修第i辆车的时间,那么等待时间总和为r[3]+(r[3]+r[5])+(r[3]+r[5]+r[6])=r[3]*3+r[5]*2+r[6]。我们发现对于一个维修人员,他修的最后一辆车的时间对答案的贡献要乘以1,修倒数第二辆车的时间要乘以2贡献给答案……以此类推。同时也意味着,对于每一个人,时间乘以1后贡献答案的只能有1辆车,时间乘以2后贡献答案的也只能有1辆车……于是我们将一个人拆成m个点,将每一辆车的时间乘以1~m连向这m个点,同时限制每个点流向t的流量只能为1:
然后问题就完美解决辣!
CODE:
#include<iostream>
#include<string>
#include<cstring>
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<stdio.h>
#include<algorithm>
using namespace std;
const int maxn=10000;
const int maxm=1000000;
const int oo=1000000000;
struct edge
{
int obj,cap,cost;
edge *Next,*rev;
} e[maxm];
edge *head[maxn];
edge *nhead[maxn];
int cur=-1;
int dis[maxn];
bool vis[maxn];
int que[maxn];
int he,ta;
int n,m,t;
void Add(int x,int y,int f,int c)
{
cur++;
e[cur].obj=y;
e[cur].cap=f;
e[cur].cost=c;
e[cur].rev=&e[cur+1];
e[cur].Next=head[x];
head[x]=e+cur;
cur++;
e[cur].obj=x;
e[cur].cap=0;
e[cur].cost=-c;
e[cur].rev=&e[cur-1];
e[cur].Next=head[y];
head[y]=e+cur;
}
void Release(int x,int y,int len)
{
if (dis[y]<=dis[x]+len) return;
dis[y]=dis[x]+len;
if (!vis[y]) vis[y]=true,ta=(ta+1)%maxn,que[ta]=y;
}
bool SPFA()
{
for (int i=0; i<=t; i++) nhead[i]=head[i],dis[i]=oo;
dis[0]=0;
vis[0]=true;
he=0,ta=1;
que[1]=0;
while (he!=ta)
{
he=(he+1)%maxn;
int node=que[he];
for (edge *p=head[node]; p; p=p->Next)
if (p->cap) Release(node,p->obj,p->cost);
vis[node]=false;
int nhe=(he+1)%maxn;
if ( nhe!=ta && dis[ que[nhe] ]>dis[ que[ta] ] ) swap(que[nhe],que[ta]);
}
return (dis[t]<oo);
}
int Dfs(int node,int maxf)
{
if ( node==t || !maxf ) return maxf;
vis[node]=true;
int nowf=0;
for (edge *&p=nhead[node]; p; p=p->Next)
{
int to=p->obj;
if ( p->cap && dis[to]==dis[node]+p->cost && !vis[to] )
{
int d=Dfs(to, min(maxf,p->cap) );
p->cap-=d;
p->rev->cap+=d;
maxf-=d;
nowf+=d;
if (!maxf) break;
}
}
vis[node]=false;
return nowf;
}
int Min_cost_flow()
{
int temp=0;
while ( SPFA() ) temp+=( dis[t]*Dfs(0,maxm) );
return temp;
}
int main()
{
freopen("2053.in","r",stdin);
freopen("2053.out","w",stdout);
scanf("%d%d",&n,&m);
t=(n+1)*m+1;
for (int i=0; i<=t; i++) head[i]=NULL;
for (int i=1; i<=m; i++) Add(0,i,1,0);
for (int i=m+1; i<t; i++) Add(i,t,1,0);
for (int i=1; i<=m; i++)
for (int j=1; j<=n; j++)
{
int v;
scanf("%d",&v);
for (int k=0; k<m; k++) Add(i,m+k*n+j,1,v*(k+1));
}
double ans=Min_cost_flow();
ans/=(double)m;
printf("%.2lf\n",ans);
return 0;
}