#include #include #include #include #include #include #include using namespace std; static const int INF = 1e9; struct MinCostFlow { struct E { //to,容量,コスト,逆辺のindex int to, cap, cost, rev; E(int t, int ca, int co, int r) { to = t; cap = ca; cost = co; rev = r; } }; //最大頂点数 int V; vector>G; MinCostFlow(int v) { V = v; G.resize(V); } //有向辺 inline void add_edge(int from, int to, int cap, int cost) { if (from == to)return; E e = E(to, cap, cost, G[to].size()); E ee = E(from, 0, -cost, G[from].size()); G[from].push_back(e); G[to].push_back(ee); } //コストを距離とみる //コスト,最小コスト vectordist, mindist; //パス復元用 vectorpre_v, pre_i; //始点から各頂点までの最小コスト void dijkstra(int s) { priority_queue< pair, vector>, greater> > que; dist.assign(V, INF); dist[s] = 0; que.push(pair(0, s)); while (!que.empty()) { pair p = que.top(); que.pop(); int v = p.second; if (dist[v] < p.first)continue; for (int i = 0; i < G[v].size(); i++) { E &e = G[v][i]; if (e.cap > 0 && dist[e.to] > dist[v] + e.cost + mindist[v] - mindist[e.to]) { dist[e.to] = dist[v] + e.cost + mindist[v] - mindist[e.to]; pre_v[e.to] = v; pre_i[e.to] = i; que.push(pair(dist[e.to], e.to)); } } } } //最小費用を返す int min_cost_flow(int s, int t, int flow) { int cost = 0; mindist.resize(V); pre_v.resize(V); pre_i.resize(V); //フローを流しつくすまで while (flow > 0) { dijkstra(s); //もう流せなかったら if (dist[t] == INF)return -1; //最小コストの更新 for (int v = 0; v < V; v++)mindist[v] += dist[v]; //最小コストパス上の最大流 int f = flow; for (int v = t; v != s; v = pre_v[v]) { f = min(f, G[pre_v[v]][pre_i[v]].cap); } flow -= f; //費用の更新 cost += f * mindist[t]; //容量の更新 for (int v = t; v != s; v = pre_v[v]) { E& e = G[pre_v[v]][pre_i[v]]; E& ee = G[v][e.rev]; e.cap -= f; ee.cap += f; } } return cost; } }; int main() { int n; cin >> n; vector>F(n); int maxF = 0; for (int i = 0; i < n; i++) { F[i].resize(n); for (int j = 0; j < n; j++) { cin >> F[i][j]; maxF = max(maxF, F[i][j]); } } for (int i = 0; i < n; i++) { for (int j = 0; j < n; j++) { F[i][j] = maxF - F[i][j]; } } MinCostFlow mcf(2 * n + 2); for (int i = 0; i < n; i++) { mcf.add_edge(0,i+1,1,0); for (int j = 0; j < n; j++) { mcf.add_edge(i+1,n+j+1,1,F[i][j]); } mcf.add_edge(n + i + 1, 2*n + 1, 1, 0); } int ans = mcf.min_cost_flow(0, 2*n+1,n); cout << (n/2)*maxF-ans/2 << endl; return 0; }