#include <bits/stdc++.h>
using namespace std;
const int INF = numeric_limits<int>::max() / 2;
struct Edge {
int to, flow, capacity, cost, reverse;
};
class WeightedNetwork {
public:
WeightedNetwork(int N, int source, int sink) : source(source), sink(sink), G(N) {}
void add_edge(int u, int v, int capacity, int cost) {
G[u].push_back({v, 0, capacity, cost, (int)G[v].size()});
G[v].push_back({u, 0, 0, -cost, (int)G[u].size() - 1});
}
int min_cost_flow(int flow = INF) {
int N = G.size();
vector<int> dist(N), prev(N), prev_edge(N);
int total_flow = 0, total_cost = 0;
while (total_flow < flow) {
fill(dist.begin(), dist.end(), INF);
dist[source] = 0;
priority_queue<pair<int, int>, vector<pair<int, int>>, greater<pair<int, int>>> pq;
pq.push({0, source});
while (!pq.empty()) {
auto [d, u] = pq.top();
pq.pop();
if (d > dist[u]) {
continue;
}
for (size_t i = 0; i < G[u].size(); ++i) {
auto [v, flow, capacity, cost, reverse] = G[u][i];
if (capacity - flow > 0 && dist[v] > dist[u] + cost) {
dist[v] = dist[u] + cost;
prev[v] = u;
prev_edge[v] = i;
pq.push({dist[v], v});
}
}
}
if (dist[sink] == INF) {
break;
}
int f = flow - total_flow;
for (int v = sink; v != source; v = prev[v]) {
f = min(f, G[prev[v]][prev_edge[v]].capacity - G[prev[v]][prev_edge[v]].flow);
}
for (int v = sink; v != source; v = prev[v]) {
auto &e = G[prev[v]][prev_edge[v]];
e.flow += f;
G[v][e.reverse].flow -= f;
total_cost += f * e.cost;
}
total_flow += f;
}
return total_cost;
}
vector<vector<Edge>> get_graph() {
return G;
}
private:
int source, sink;
vector<vector<Edge>> G;
};
int main() {
assert(freopen64("cc.in", "r", stdin));
assert(freopen64("cc.out", "w", stdout));
int N;
cin >> N;
int source = 2 * N, sink = 2 * N + 1;
WeightedNetwork network(2 * N + 2, source, sink);
for (int i = 0; i < N; i++) {
network.add_edge(source, i, 1, 0);
network.add_edge(i + N, sink, 1, 0);
}
for (int i = 0; i < N; i++) {
for (int j = 0; j < N; j++) {
int cost;
cin >> cost;
network.add_edge(i, j + N, 1, cost);
}
}
cout << network.min_cost_flow() << '\n';
return 0;
}