#include<bits/stdc++.h>
#define INF 2e9+7
using namespace std;
int n,m;
struct Front_star{
int u,v,w,c,nxt;
}e[400010];
int cnt=1;
int first[20001]={0};
void addedge(int u,int v,int w,int c){
cnt++;
e[cnt].u=u;
e[cnt].v=v;
e[cnt].w=w;
e[cnt].c=c;
e[cnt].nxt=first[u];
first[u]=cnt;
}
void add(int u,int v,int w,int c){
addedge(u,v,w,c);
addedge(v,u,0,-c);
}
int a[2000][2000]={0};
const int S=0,T=1001;
inline int sum(int x,int y){
return (x-1)*m+y+m;
}
int dis[20001]={0};
int pre[20001]={0};
int inqueue[20001]={0};
int vis[20001]={0};
int SPFA(){
queue<int> q;
for(int i=S;i<=T;i++){
dis[i]=INF;
pre[i]=0;
inqueue[i]=0;
}
q.push(S);
dis[S]=0;
while(!q.empty()){
int x=q.front();
// cout<<"x="<<x<<endl;
q.pop();
inqueue[x]=0;
for(int i=first[x];i;i=e[i].nxt){
int v=e[i].v;
if(dis[v]>dis[x]+e[i].c&&e[i].w){
dis[v]=dis[x]+e[i].c;
pre[v]=i;
if(!inqueue[v]){
inqueue[v]=1;
q.push(v);
}
}
}
}
return dis[T]!= INF;
}
int Mcmf(){
int ans=0;
while(SPFA()){
int s=INF;
for(int i=pre[T];i;i=pre[e[i^1].v]) {s=min(s,e[i].w);}
for(int i=pre[T];i;i=pre[e[i^1].v]){
e[i].w-=s;
e[i^1].w+=s;
}
ans+=s*dis[T];
// cout<<ans<<endl;
// cout<<"working!!!!"<<endl;
}
// exit(0);
return ans;
}
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++){
// cout<<"31265"<<endl;
add(S,i,1,0);//S到车流1费0
for(int j=1;j<=n;j++){
scanf("%d",&a[i][j]);
}
}
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
add(sum(i,j),T,1,0);//工人(M*N)到T流1费0
}
}
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
for(int k=1;k<=m;k++) add(j,sum(i,k),1,k*a[j][i]);//把每个师傅拆成n个,每个代表倒数k个修,这样与车连边,费用就是上面的辣个式子。
}
}
printf("%.2lf",(double)Mcmf()*1.0/m);
}
半裸的最小费用最大流
构图:把人拆成N个点,表示N个时间段的工人。这样一共M*N个工人。然后把N辆车和M*N个工人全部连起来(完全二分图),权值是工人时间段编号*输入的时间,表示需要等待的时间。S到车流1费0,工人(M*N)到T流1费0。这样加起来后满流的最小费用/N答案了。