#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int, int> ii;
const int INF = 1e9;
const ll LINF = 1e18;
const int M = 1e4 + 5;
int n, m;
int a[16][M];
bool ok[16][16];
bool dp[1 << 16][16];
bool check(int k) {
// ok[x][y] = 1 nếu hàng y có thể đặt kề ngay phía dưới hàng x, 0 nếu ngược lại
for (int x = 0; x < n; x++) {
for (int y = 0; y < n; y++) {
ok[x][y] = true;
for (int i = 0; i < m; i++) {
ok[x][y] &= (abs(a[y][i] - a[x][i]) >= k);
}
}
}
for (int first_row = 0; first_row < n; first_row++) { // Hàng đầu tiên
// dp[mask][last_row] = 1 nếu tồn tại một cách sắp xếp các hàng có trong tập mask, với last_row là hàng cuối cùng
// sao cho thoả mãn điều kiện ok[x][y] với mọi hàng x, y được xếp kề nhau
// và hàng y được xếp kề ngay phía dưới hàng x
memset(dp, 0, sizeof dp);
dp[1 << first_row][first_row] = 1;
for (int mask = 0; mask < (1 << n); mask++) {
for (int x = 0; x < n; x++) {
if (dp[mask][x] == 0) continue;
for (int y = 0; y < n; y++) {
if ((mask >> y) & 1) continue;
if (!ok[x][y]) continue;
int next_mask = mask | (1 << y);
dp[next_mask][y] = 1;
}
}
}
// Một cách sắp xếp thoả mãn khi thoả mãn dp = 1 (điều kiện cần)
// và thoả thêm điều kiện của first_row và last_row (điều kiện đủ)
for (int last_row = 0; last_row < n; last_row++) {
if (dp[(1 << n) - 1][last_row]) {
bool valid = true;
for (int i = 0; i + 1 < m; i++) {
if (abs(a[first_row][i + 1] - a[last_row][i]) < k) {
valid = false;
break;
}
}
if (valid) return true;
}
}
}
return false;
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
cin >> n >> m;
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) cin >> a[i][j];
}
int l = 0, r = 1e9, ans = -1;
while (l <= r) {
int mid = (l + r) >> 1;
if (check(mid)) {
ans = mid;
l = mid + 1;
}
else {
r = mid - 1;
}
}
cout << ans << '\n';
}