import std.stdio, std.conv, std.string, std.bigint;
import std.math, std.random, std.datetime;
import std.array, std.range, std.algorithm, std.container, std.format;
string read(){ static string[] ss; while(!ss.length) ss = readln.chomp.split; string res = ss[0]; ss.popFront; return res; }


/*

ビットDP


x[m]: ビットマップmで表されるうさぎたちがなすグループの得点

m = (1<<i) | m' であるとき
x[m] = x[m'] + sum(j; (1<<i) & j)(a[i, j])


y[m]: ビットマップmで表されるうさぎたちを最適にグループ分けしたときの得点


y[m] = max(x[m], max(m'; m' subsetof m)(y[m'] + y[m - m']))
※subsetof は、 m - m' == m ^ m' として実装できる

これは(2^16)×(2^16)なのでオーダー的にはTLEだが、がんばれば通るだろう
定数倍をがんばる

*/


long tmp, dt; // 高速化

void main(){

 int n = read.to!int;

 long[][] as = new long[][](16, 16);
 foreach(i; 0 .. n) foreach(j; 0 .. n) as[i][j] = read.to!long;

 debug StopWatch sw;
 debug sw.start();

 long[] xs = new long[](1<<n);
 foreach(i; 0 .. n){
  foreach(m; 0 .. 1<<i){
   xs[1<<i | m] = xs[m];
   foreach(j; 0 .. i){
    if(1<<j & m) xs[1 << i | m] += as[i][j];
   }
  }
 }
 debug writeln("xs:", xs[0 .. 1<<n]);

 long[] ys = new long[](1<<n);
 foreach(m; 0 .. 1<<n){
  ys[m] = xs[m];

  int t = 1, u = m - 1; // u は m - t のこと
  while(t < u){
   if((m ^ t) == u){ // t が m のサブセットであるとき
    tmp = ys[t] + ys[u];
    if(tmp > ys[m]) ys[m] = tmp;
    t += 1, u -= 1;
   }
   else{
	// t の最後の桁と同じだけ上げる(例：10110100 がだめだったら、100の位がだめということなので、+100する）
    dt = (t ^ (t - 1)) & t;
    t += dt, u -= dt; 
   }
  }

 }
 debug writeln("ys:", ys[0 .. 1<<n]);

 ys[(1<<n) - 1].writeln;

 debug writeln(sw.peek().msecs);

}