#include <iostream> //最小费用最大流
#include <memory.h>
using namespace std;
const int N = 100;
int map[2 * N + 2][2 * N + 2]; //0为源点 1-n老师 n+1-2n类型 2N+1为汇点 保留的是边的权值(费用)
int flow[2 * N + 2][2 * N + 2]; //流量
int path[2 * N + 2]; //记录节点i在此次增广中的前驱
int n;
int ans = 0;
void input() {
cin >> n;
int x;
for (int i = 1; i <= n; i++)
for (int j = 1; j <= n; j++) {
cin >> x;
map[i][j + n] = x;
map[j + n][i] = -x;
flow[i][j + n] = 1;
}
for (int i = 1; i <= n; i++) {
map[0][i] = 0;
flow[0][i] = 1;
}
for (int i = 1; i <= n; i++) {
map[i + n][2 * n + 1] = 0;
flow[i + n][2 * n + 1] = 1;
}
return;
}
int shrtpth[2 * N + 2]; //源点到其余点的最短路径
int frm[2 * N + 1]; //记录前驱
int Queue[5 * N + 1]; //队列
bool work() //spfa找增广路
{
for (int i = 0; i <= 2 * N + 1; i++)
shrtpth[i] = -32767;
shrtpth[0] = 0;
int R, L;
R = L = 1;
Queue[1] = 0;
for (; L <= R; L++) {
for (int i = 0; i <= 2 * n + 1; i++) //枚举每次被松弛的顶点即边[Queue[L]][i]
if (flow[Queue[L]][i] > 0) //存在流量
if (map[Queue[L]][i] + shrtpth[Queue[L]] > shrtpth[i]) {
shrtpth[i] = map[Queue[L]][i] + shrtpth[Queue[L]];
frm[i] = Queue[L]; //记前驱
Queue[++R] = i; //i入队列
}
}
if (shrtpth[2 * n + 1] == -32767) return false;
int i = 2 * n + 1;
ans += shrtpth[2 * n + 1];
while (i != 0) //每次找前驱
{
flow[frm[i]][i]--;
flow[i][frm[i]]++;
i = frm[i];
}
return true;
}
void all_clear() {
memset(flow, 0, sizeof(flow));
return;
}
int main() {
all_clear();
input();
while (work())
;
cout << ans;
return 0;
}