BZOJ 1070 修车
2018.02.25
2018.02.25
给你一些人和一些车,人修车,每个人修每辆车的时间不同,每个人同时只能修理一台车。问最终的所有人的等待总时长的平均值的最小值。
拆点+费用流,第一次做这种套路的题。
平均值当然可以忽略,变成求总时长的最小值。
考虑对车进行贡献的计算。
第$i$个人修倒数第$j$辆车的总时间为$j*f[k][i]$(我就是在这里ik搞反了WA了一次= =)
所以将一个人拆成车数量个点,之后S连车,车连人(的车数量个点),人连T,跑一边最小费用最大流就OK了。
#include <queue>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define N 10010
#define M 500000
int m,n,t[100][10],S=0,T=4999,dis[N],pre[N],frm[N],ans;
int to[M],nxt[M],val[M],cst[M],head[N],tot=1;
queue<int>q;
inline void AE(int x,int y,int a,int b)
{
to[++tot]=y,nxt[tot]=head[x],head[x]=tot,val[tot]=a,cst[tot]=b;
to[++tot]=x,nxt[tot]=head[y],head[y]=tot,val[tot]=0,cst[tot]=-b;
}
bool SPFA()
{
q.push(S);
memset(dis,0x3f,sizeof dis),dis[S]=0;
while(!q.empty())
{
int pos=q.front();q.pop();
for(int i=head[pos];i;i=nxt[i])
if(val[i]&&dis[to[i]]>dis[pos]+cst[i])
{
dis[to[i]]=dis[pos]+cst[i];
frm[to[i]]=pos,pre[to[i]]=i;
q.push(to[i]);
}
}
return dis[T]!=0x3f3f3f3f;
}
int main()
{
scanf("%d%d",&m,&n);
for(int i=1;i<=n;++i)
for(int j=1;j<=m;++j)
scanf("%d",&t[i][j]);
for(int i=1;i<=n;++i) AE(S,i,1,0);
int tmp=n;
for(int i=1;i<=m;++i)
for(int j=1;j<=n;++j)
{
++tmp;
for(int k=1;k<=n;++k) AE(k,tmp,1,j*t[k][i]);
AE(tmp,T,1,0);
}
while(SPFA())
{
ans+=dis[T];
for(int i=T;i!=S;i=frm[i]) val[pre[i]]--,val[pre[i]^1]++;
}
printf("%.2lf",1.0*ans/n);
return 0;
}