/*
Catalin-Stefan Tiseanu
Pre-written code is assembled from various sources found online.
Big thanks to the community !
*/
// Pre-written code below
using namespace std;
#include <algorithm>
#include <bitset>
#include <cassert>
#include <cctype>
#include <climits>
#include <cmath>
#include <complex>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <ctime>
#include <deque>
#include <fstream>
#include <functional>
#include <iomanip>
#include <iostream>
#include <limits>
#include <list>
#include <map>
#include <numeric>
#include <queue>
#include <set>
#include <sstream>
#include <stack>
#include <string>
#include <utility>
#include <vector>
//#define DEBUG
#ifdef DEBUG
#define debug(args...) {dbg,args; cerr<<endl;}
#else
#define debug(args...) // Just strip off all debug tokens
#endif
struct debugger
{
template<typename T> debugger& operator , (const T& v)
{
cerr<<v<<" ";
return *this;
}
} dbg;
// templates
template<typename T> int size(const T& c) { return int(c.size()); }
template<typename T> T abs(T x) { return x < 0 ? -x : x; }
template<typename T> T sqr(T x) { return x*x; }
template<typename T> bool remin(T& x, T y) { if (x <= y) return false; x = y; return true; }
template<typename T> bool remax(T& x, T y) { if (x >= y) return false; x = y; return true; }
// misc
#define EPSILON 1e-7
// types
typedef long long int64;
typedef unsigned long long uint64;
// shortcuts
#define all(_xx) _xx.begin(), _xx.end()
#define pb push_back
#define vi vector<int>
#define vpii vector<pair<int,int> >
#define pii pair<int,int>
#define mp(XX, YY) make_pair(XX, YY)
#define fi first
#define se second
#define SS stringstream
// for loops
#define re(II, NN) for (int II(0), _NN(NN); (II) < (NN); ++(II))
#define fod(II, XX, YY) for (int II(XX), _YY(YY); (II) >= (_YY); --(II))
#define fo(II, XX, YY) for (int II(XX), _YY(YY); (II) <= (_YY); ++(II))
// Useful hardware instructions
#define bitcount __builtin_popcount
#define gcd __gcd
// Useful all around
#define checkbit(n,b) ( (n >> b) & 1)
#define DREP(a) sort(all(a));a.erase(unique(all(a)),a.end())
#define INDEX(arr,ind) (lower_bound(all(arr),ind)-arr.begin())
// Note: Thanks to Misof for the Mincost-Maxflow code below
template<class T, class U>
class MincostMaxflow {
public:
class edge {
public:
int source, destination;
T capacity, residue;
U cost;
edge(int s, int d, T cap, T res, U cost)
: source(s), destination(d), capacity(cap), residue(res), cost(cost) { }
};
vector< vector<int> > V;
vector<edge> E;
void clear();
void addArc(int source, int destination, T capacity, U cost);
pair<T,U> getFlow(int source, int sink);
};
template<class T, class U> void MincostMaxflow<T,U>::clear() { V.clear(); E.clear(); }
template<class T, class U>
void MincostMaxflow<T,U>::addArc(int source, int destination, T capacity, U cost) {
// cout << "addArc " << source << " " << destination << " " << capacity << " " << cost << endl;
int e = E.size();
if (source >= int(V.size())) V.resize(source+1);
if (destination >= int(V.size())) V.resize(destination+1);
V[source].push_back( e );
V[destination].push_back( e+1 );
E.push_back(edge(source,destination,capacity,capacity,cost));
E.push_back(edge(destination,source,capacity,0,-cost));
}
template<class T, class U>
pair<T,U> MincostMaxflow<T,U>::getFlow(int source, int sink) {
if (source >= int(V.size())) V.resize(source+1);
if (sink >= int(V.size())) V.resize(sink+1);
int N = V.size(), M = E.size();
T flowSize = 0;
U flowCost = 0;
T Tinfinity = 1; while (2*Tinfinity > Tinfinity) Tinfinity *= 2;
U Uinfinity = 1; while (2*Uinfinity > Uinfinity) Uinfinity *= 2;
U Uepsilon = 1; for (int i=0; i<30; i++) Uepsilon /= 2;
vector<T> flow(M,0);
vector<U> potential(N,0);
while (1) {
// use dijkstra to find an augmenting path
vector<int> from(N,-1);
vector<U> dist(N,Uinfinity);
priority_queue< pair<U,int>, vector<pair<U,int> >, greater<pair<U,int> > > Q;
Q.push(make_pair(0,source));
from[source]=-2;
dist[source] = 0;
while (!Q.empty()) {
U howFar = Q.top().first;
int where = Q.top().second;
Q.pop();
if (dist[where] < howFar) continue;
for (int i=0; i<int(V[where].size()); i++) {
if (E[ V[where][i] ].residue == 0) continue;
int dest = E[ V[where][i] ].destination;
U cost = E[ V[where][i] ].cost;
if (dist[dest] > dist[where] + potential[where] - potential[dest] + cost + Uepsilon) {
dist[dest] = dist[where] + potential[where] - potential[dest] + cost;
from[dest] = V[where][i];
Q.push( make_pair( dist[dest],dest ));
}
}
}
// update vertex potentials
for (int i=0; i<N; i++) {
if (dist[i]==Uinfinity) potential[i] = Uinfinity;
else if (potential[i]<Uinfinity) potential[i] += dist[i];
}
// if there is no path, we are done
if (from[sink] == -1) return make_pair(flowSize,flowCost);
// construct an augmenting path
T canPush = Tinfinity;
int where = sink;
while (1) {
if (from[where]==-2) break;
canPush = min(canPush, E[ from[where] ].residue );
where = E[ from[where] ].source;
}
// update along the path
where = sink;
while (1) {
if (from[where]==-2) break;
E[ from[where] ].residue -= canPush;
E[ from[where]^1 ].residue += canPush;
flowCost += canPush * E[ from[where] ].cost;
where = E[ from[where] ].source;
}
flowSize += canPush;
}
return make_pair(-1,47);
}
// Code written during the competition below
int N, A[1111][1111];
void my_test() {
N = 100;
fo (i, 1, N)
fo (j, 1, N)
A[i][j] = rand() % 10001 + 1;
}
int main() {
freopen("cc.in", "r", stdin);
freopen("cc.out", "w", stdout);
MincostMaxflow <int, int> MF;
scanf("%d", &N);
fo(i, 1, N)
fo (j, 1, N)
scanf("%d", &A[i][j]);
//my_test();
fo (i, 1, N)
fo (j, 1, N)
MF.addArc(i, j + N, 1, A[i][j]);
fo (i, 1, N) MF.addArc(0, i, 1, 0);
fo (i, 1, N) MF.addArc(i + N, 2 * N + 1, 1, 0);
pii result = MF.getFlow(0, 2 * N + 1);
printf("%d\n", result.se);
re(i, size(MF.E)) {
if (!MF.E[i].residue && MF.E[i].cost >= 0 &&
MF.E[i].source > 0 &&
MF.E[i].source < 2 * N + 1 &&
MF.E[i].destination > 0 &&
MF.E[i].destination < 2 * N + 1) {
debug(MF.E[i].source, " ", MF.E[i].destination, " ", MF.E[i].cost);
}
}
return 0;
}