[SCOI2007] 修车
考虑将修车师傅放在一边,顾客放在一边。
对于第 \(i\) 辆车,让第 \(j\) 个修车师傅来修,放在了倒数第 \(l\) 个,那么他产生的贡献即为 \(t_{i,j}\times l\)。
我们可以将每个修车师傅拆成 \(n\) 个点,第 \(l\) 个点表示修车师傅的倒数第 \(l\) 个位置,跑费用流即可。
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=1005,M=1e5+5;
int n,m,s,t,k=1,h[N],vis[N];
int to[M],nxt[M],w[M],f[M];
int lst[N],flw[N],dis[N];
void add(int x,int y,int z,int a){
w[++k]=z;f[k]=a;to[k]=y;
nxt[k]=h[x];h[x]=k;
f[++k]=-a;to[k]=x;
nxt[k]=h[y];h[y]=k;
}queue<int>q;
int spfa(){
while(q.size()) q.pop();
memset(lst,-1,sizeof(lst));
memset(vis,0,sizeof(vis));
memset(dis,127,sizeof(dis));
flw[s]=1e9;dis[s]=0;q.push(s);
while(q.size()){
int x=q.front();
q.pop();vis[x]=0;
for(int i=h[x];i;i=nxt[i]){
int y=to[i],vl=w[i];
if(vl&&dis[y]>dis[x]+f[i]){
lst[y]=i;
flw[y]=min(flw[x],vl);
dis[y]=dis[x]+f[i];
if(!vis[y])
q.push(y),vis[y]=1;
}
}
}return lst[t]!=-1;
}int mxflw,mncst;
void MCMF(){
while(spfa()){
mxflw+=flw[t];
mncst+=dis[t]*flw[t];
for(int i=t;i!=s;i=to[lst[i]^1])
w[lst[i]]-=flw[t],w[lst[i]^1]+=flw[t];
}
}signed main(){
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
cin>>m>>n;t=n+n*m+1;
for(int i=1;i<=m;i++)
for(int j=1;j<=n;j++)
add(s,i*n+j,1,0);
for(int i=1;i<=n;i++) add(i,t,1,0);
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++){
int x;cin>>x;
for(int l=1;l<=n;l++)
add(j*n+l,i,1,l*x);
}
MCMF();printf("%.2lf",mncst*1.0/n);
return 0;
}//spfa:它没有死透