#include <bits/stdc++.h>
using namespace std;
long long rdtsc() {
  long long tmp;
  asm("rdtsc" : "=A"(tmp));
  return tmp;
}
inline int myrand() { return abs((rand() << 15) ^ rand()); }
inline int rnd(int x) { return myrand() % x; }
const int INF = (int)1.01e9;
const long double EPS = 1e-9;
void precalc() {}
const int maxn = 20;
int a[maxn][maxn], b[maxn][maxn];
int n;
bool read() {
  if (scanf("%d", &n) < 1) {
    return 0;
  }
  for (int i = 0; i < n; ++i) {
    for (int j = 0; j < n; ++j) {
      scanf("%d", &a[i][j]);
    }
  }
  for (int i = 0; i < n; ++i) {
    for (int j = 0; j < n; ++j) {
      scanf("%d", &b[i][j]);
    }
  }
  return 1;
}
int d[maxn][maxn];
int addr[maxn], addc[maxn];
int getRow[maxn];
int minCol[maxn], minPos[maxn];
int used[maxn];
int toadd;
int res;
void oneStep(int r0, int a[maxn][maxn]);
int taddr[maxn][maxn], taddc[maxn][maxn];
int tgetRow[maxn][maxn];
void gen(int pos, int bal) {
  if (pos == n) {
    int sum = 0;
    for (int i = 0; i < n; ++i) {
      sum += addr[i];
    }
    for (int i = 0; i < n; ++i) {
      sum += addc[i];
    }
    res = max(res, sum - toadd * n);
    return;
  }
  memcpy(tgetRow[pos], getRow, sizeof(int) * n);
  memcpy(taddr[pos], addr, sizeof(int) * n);
  memcpy(taddc[pos], addc, sizeof(int) * n);
  if (bal + 1 <= (n - pos - 1)) {
    for (int i = 0; i < n; ++i) {
      d[pos][i] = a[pos][i];
    }
    oneStep(pos, d);
    gen(pos + 1, bal + 1);
    memcpy(getRow, tgetRow[pos], sizeof(int) * n);
    memcpy(addr, taddr[pos], sizeof(int) * n);
    memcpy(addc, taddc[pos], sizeof(int) * n);
  }
  if (bal - 1 >= -(n - pos - 1)) {
    for (int i = 0; i < n; ++i) {
      d[pos][i] = b[pos][i];
    }
    oneStep(pos, d);
    gen(pos + 1, bal - 1);
    memcpy(getRow, tgetRow[pos], sizeof(int) * n);
    memcpy(addr, taddr[pos], sizeof(int) * n);
    memcpy(addc, taddc[pos], sizeof(int) * n);
  }
}
void oneStep(int r0, int a[maxn][maxn]) {
  {
    for (int i = 0; i < n; ++i) {
      minCol[i] = INF;
      minPos[i] = -1;
      used[i] = 0;
    }
    int r, c;
    for (r = r0, c = -1; r != -1;) {
      int newc = -1;
      for (int curc = 0; curc < n; ++curc) {
        if (used[curc]) continue;
        int value = a[r][curc] + addr[r] + addc[curc];
        if (minCol[curc] > value) {
          minCol[curc] = value;
          minPos[curc] = c;
        }
        if (newc == -1 || minCol[newc] > minCol[curc]) newc = curc;
      }
      {
        int value = minCol[newc];
        addr[r0] -= value;
        for (int curc = 0; curc < n; ++curc) {
          if (used[curc]) {
            addc[curc] += value;
            addr[getRow[curc]] -= value;
          } else
            minCol[curc] -= value;
        }
      }
      used[newc] = 1;
      c = newc, r = getRow[c];
    }
    for (; minPos[c] != -1; c = minPos[c]) getRow[c] = getRow[minPos[c]];
    getRow[c] = r0;
  }
  if (0) {
    for (int i = 0; i <= r0; ++i)
      for (int j = 0; j < n; ++j) assert(a[i][j] + addr[i] + addc[j] >= 0);
  }
}
void solve() {
  toadd = 0;
  for (int i = 0; i < n; ++i) {
    for (int j = 0; j < n; ++j) {
      toadd = min(toadd, -a[i][j]);
      toadd = min(toadd, -b[i][j]);
    }
  }
  for (int i = 0; i < n; ++i) {
    for (int j = 0; j < n; ++j) {
      a[i][j] = -a[i][j] - toadd;
      b[i][j] = -b[i][j] - toadd;
      assert(a[i][j] >= 0);
      assert(b[i][j] >= 0);
    }
  }
  res = 0;
  for (int i = 0; i < n; ++i) {
    getRow[i] = -1;
    addr[i] = addc[i] = 0;
  }
  gen(0, 0);
  printf("%d\n", res);
}
int main() {
  srand(rdtsc());
  precalc();
  while (1) {
    if (!read()) {
      break;
    }
    solve();
  }
  return 0;
}
