#include <bits/stdc++.h>
#include <bits/extc++.h>
typedef long long int ll;
typedef unsigned long long int ull;
typedef std::pair<int,int> pii;
typedef std::vector<int> vec;
using namespace std;
#define __rep(i,a,b,c) for(int i=(a);i!=(b);i+=(c))
#define _rep(i,a,b) for(int i=(a);i>=(b);i--)
#define rep(i,a,b) for(int i=(a);i<=(b);i++)
#define Yes cout << "Yes" << endl
#define No cout << "No" << endl
#define End return 0
#define blockId(x) ((x)/BLOCK_SIZE)
namespace Anschluss_zeit {
const int MAXN = 500000;
namespace IO {
void read(){}
template<typename T,typename... Types>
void read(T& first, Types&... args){
T x = 0, f = 1;char c = getchar();
while (!isdigit(c)) {if (c == '-') f = -1;c = getchar();}
while (isdigit(c))x = (x << 3) + (x << 1) + (c^'0'), c = getchar();
first = x * f;
read(args...);
}
template<typename T>
void _print(T x) {if (x < 0) x = (~x + 1), putchar('-');if (x > 9) _print(x / 10);putchar(x % 10 + '0');}
void _print(string s) {int l = s.length();for (int i = 0; i < l; i++)putchar(s[i]);}
void print(){}
void eoln(){putchar('\n');}
template<typename T,typename... Types>
void print(T first, Types... args){_print(first);putchar(' ');print(args...);}
template<typename T,typename... Types>
void println(T first, Types... args){print(first,args...);eoln();}
};
};
using namespace Anschluss_zeit;
using namespace IO;
#define INF 1e16
struct edge{
int nxt, to;
ll c, w;
//表示 一条连往v的边的容量为c,代价w
} e[10 * MAXN];
int cnt = 1, then[MAXN];
ll dis[MAXN];
bitset<MAXN> vis, inq;
queue<int> q;
void _add(int u, int v, ll c, ll w){e[++cnt]=(edge){then[u], v, c, w}; then[u]=cnt;}
void add(int u, int v, ll c, ll w){_add(u, v, c, w); _add(v, u, 0, -w);}
int S, T, cur[MAXN];
bool dinic_bfs(){//SPFA
while(!q.empty()) q.pop();
copy(then, then + MAXN, cur);
fill(dis, dis + MAXN, INF);
dis[S] = 0; q.push(S); inq[S] = 1;
inq.reset();
while(!q.empty()){
int t = q.front(); q.pop();
inq[S] = 0;
for(int i = cur[t]; i; i = e[i].nxt){
int c = e[i].c, v = e[i].to;
if(c > 0 && dis[v] > dis[t] + e[i].w){
dis[v] = dis[t] + e[i].w;
if(!inq[v]) q.push(v), inq[v] = 1;
}
}
}
return dis[T] < INF;
}
ll dinic_dfs(int x, ll flow){
if(x == T) return flow;
ll rf = flow; vis[x] = 1;
for(int i = cur[x]; i; i = e[i].nxt){
if(rf <= 0) break;
cur[x] = i;
ll rf_x = e[i].c; int v = e[i].to;
if(rf_x > 0 && dis[v] == dis[x] + e[i].w && !vis[v]){
ll transfer = dinic_dfs(v, min(rf_x, rf));
rf -= transfer;
e[i].c -= transfer;
e[i ^ 1].c += transfer;
}
}
vis[x] = 0;
return flow - rf;
}
pii dinic(){
int mx, mn;
while(dinic_bfs()){
int flow = dinic_dfs(S, INF);
mx += flow;
mn += dis[T] * flow;
}
return make_pair(mx, mn);
}
int n, m, c[100][100], id;
signed main() {
read(m, n);
S = 0; T = n * m + n + 1;
rep(i, 1, n){
rep(j, 1, m){
read(c[i][j]);
}
}
rep(i, 1, n * m) add(S, i, 1, 0);
rep(i, 1, m){
rep(j, 1, n){
rep(k, 1, n){
add((i - 1) * n + k, i * n + j + n * m, 1, c[j][i] * k);
}
}
}
rep(i, 1, n) add(n * m + i, T, 1, 0);
printf("%.2lf",(double) dinic().second / (double) n);
return 0;
}
Dinic算法。