网络流,将维修人员拆成N个,记为P(i,j),然后每辆车对P(i,j)连一条容量为1,权为j*time[i][j]的边,表示让第i个维修人员在倒数第j的位置修这辆车,于是会导到剩下的所有人多等j*time[i][j]的时间
const int N = 700;
struct Edge {
int V, Val, Cost;
Edge *Next, *Pair;
Edge() {}
Edge(int _V, int _Val, int _Cost, Edge *_Next) :
V(_V), Val(_Val), Cost(_Cost), Next(_Next) {}
} *Fir[N];
int S, T, Tot, Dis[N], Ans, Sum;
deque<int> Q;
bool Vis[N];
int n, m;
#define Man(x, y) (((x) - (1)) * (n) + (y))
#define Car(x) ((n) * (m) + (x))
inline void Ins(int x, int y, int val, int cost) {
Fir[x] = new Edge(y, val, cost, Fir[x]);
Fir[y] = new Edge(x, 0, -cost, Fir[y]);
Fir[x]->Pair = Fir[y], Fir[y]->Pair = Fir[x];
}
inline void Input() {
scanf("%d%d", &m, &n);
S = n * m + n + 1, Tot = T = S + 1;
For(i, 1, n)
For(j, 1, m) {
int x;
scanf("%d", &x);
For(k, 1, n) Ins(Man(j, k), Car(i), 1, x * k);
}
For(i, 1, m)
For(j, 1, n) Ins(S, Man(i, j), 1, 0);
For(i, 1, n) Ins(Car(i), T, 1, 0);
}
inline bool Spfa() {
clr(Dis, 63), Q.clear();
for(Q.pub(T), Dis[T] = 0; sz(Q);) {
int x = Q.front(), v, t;
Q.pof();
for(Edge *Tab = Fir[x]; Tab != NULL; Tab = Tab->Next)
if(Tab->Pair->Val && Dis[v = Tab->V] > (t = Dis[x] - Tab->Cost))
(Dis[v] = t) <= Dis[sz(Q) ? Q.front() : 0] ? Q.puf(v) : Q.pub(v);
}
For(i, 1, Tot)
for(Edge *Tab = Fir[i]; Tab != NULL; Tab = Tab->Next)
Tab->Cost += Dis[Tab->V] - Dis[i];
return Sum += Dis[S], Dis[S] < Dis[0];
}
inline int Aug(int x, int L) {
if(x == T) return Ans += Sum * L, L;
Vis[x] = 1;
int N = L, v;
for(Edge *Tab = Fir[x]; Tab != NULL; Tab = Tab->Next)
if(Tab->Val && !Tab->Cost && !Vis[v = Tab->V]) {
int t = Aug(v, min(Tab->Val, N));
Tab->Val -= t, N -= t, Tab->Pair->Val += t;
if(!N) break;
}
return L - N;
}
inline void Solve() {
while(Spfa())
for(clr(Vis, 0); Aug(S, INF) > 0; clr(Vis, 0));
printf("%.2lf\n", (DB) Ans / n);
}
int main() {
#ifndef ONLINE_JUDGE
SETIO("1070");
#endif
Input();
Solve();
return 0;
}