先挖一个坑在这里,等什么时候心情好了再去填,zkw网络流:http://www.artofproblemsolving.com/community/c1368h1020435
首先源点和每一个车建边,然后拆人的点,每个人拆成他本身倒数第i个修车的点,将车和人建边的时候费用就是k*a[i][j]
也就是当前修车时间加上对后面修车的状态已影响就好了。最后向汇点建边,还用的是最简单的费用流,汗。
#include<cstdio>
#include<cstring>
#include<iostream>
#define maxn 1000020
using namespace std;
int n,m,a[71][71],head[maxn],tot;
struct edge{
int v,next,w,c;
edge(int x=0,int y=0,int z=0,int h=0):v(x),next(y),w(z),c(h){}
}e[maxn];
void adde(int a,int b,int f,int c){
e[tot]=edge(b,head[a],f,c);
head[a]=tot++;
e[tot]=edge(a,head[b],0,-c);
head[b]=tot++;
}
int S=0,T,q[maxn],d[maxn],from[maxn],vis[maxn],ef[maxn];
bool spfa(){
for(int i=0;i<=T;i++)d[i]=1e9,vis[i]=0;
int l=0,r=1;q[0]=d[0]=0;
while(l<r){
int u=q[l++];
vis[u]=0;
for(int v,i=head[u];i!=-1;i=e[i].next){
v=e[i].v;if(e[i].w<=0)continue;
if(d[v]>d[u]+e[i].c){
d[v]=d[u]+e[i].c;
from[v]=u;ef[v]=i;
if(!vis[v]){
q[r++]=v;
vis[v]=1;
}
}
}
}
return d[T]<1e9;
}
int ans=0;
void solve(){
while(spfa()){
int Min=1e9;
for(int i=T;i!=S;i=from[i]){
Min=min(Min,e[ef[i]].w);
}
for(int i=T;i!=S;i=from[i]){
ans+=e[ef[i]].c;e[ef[i]].w-=Min;e[ef[i]^1].w+=Min;
}
}
printf("%.2lf",ans*1.0/n);
}
int main(){
memset(head,-1,sizeof(head));
scanf("%d%d",&m,&n);
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
scanf("%d",&a[i][j]);
}
}
for(int i=1;i<=n;i++){
adde(S,i,1,0);
for(int j=1;j<=m;j++){
for(int k=1;k<=n;k++){
adde(i,n*j+k,1,a[i][j]*k);
}
}
}
T=n*(m+1)+1;
for(int i=1;i<=m;i++){
for(int j=1;j<=n;j++){
adde(n*i+j,T,1,0);
}
}
solve();
return 0;
}