// Original C++ solution (slower):
#include <bits/stdc++.h>

#include <ext/pb_ds/assoc_container.hpp>

#define fastio() ios_base::sync_with_stdio(false);

#define rmod(x, y) ((((x)%(y))+(y))%(y))

using namespace std;

using namespace __gnu_pbds;

typedef long long ll;

typedef long double ld;

template<typename T, typename R = null_type, typename C = less<T>>

tree<T, R, C, rb_tree_tag, tree_order_statistics_node_update> ordered_set(){

	return tree<T, R, C, rb_tree_tag, tree_order_statistics_node_update>();

}



const ll inf = 1e17;

const int maxn = 17, maxm = (1<<16)+5;

bool vis[maxn][maxm];

int n;

ll a[maxn][maxn], val[maxm], dp[maxm];



ll solve(int mask){

	if (dp[mask] != -1) return dp[mask];

	ll ret = -inf;

	for(int i = mask; i>0; i=(i-1)&mask){

		ret = max(ret, val[i]+solve(mask^i));

	}

	return dp[mask] = ret;

}





int main(){

	memset(dp, -1, sizeof(dp));

	scanf("%d", &n);

	for(int i = 0; i<n; i++)

		for(int j = 0; j<n; j++) scanf("%lld", &a[i][j]);

	for(int i = 0; i<(1<<n); i++){

		if(__builtin_popcount(i) <= 1) val[i] = 0, dp[i] = 0;

		else for(int j = 0; j<n; j++) if ((i>>j)&1){

			for(int k = j+1; k<n; k++)

				if ((i>>k)&1) val[i]+=a[j][k];

		}

	}

	printf("%lld\n", solve((1<<n)-1));

	

	return 0;

}


// Optimized solution to the same problem (faster):
#include <bits/stdc++.h>

#define ull unsigned long long

#define ll long long

#define endl '\n'

#define pb push_back

#define mod 1000000007

#define mp make_pair

#define pi acos(-1)

#define line cout<<"----------------------------"<<endl

#define fast ios_base::sync_with_stdio(false); cin.tie(NULL)

#define filein freopen("input.txt","r",stdin)

#define fileout freopen("output.txt","w",stdout)

using namespace std;

const ll  mx=3009;

ll pre[1LL<<17]; 

int ara[17][17]; 

ll dp[1<<17] ; 

int main()

{

	fast; 

	int  n;

	cin>>n; 

	for(int  i=0;i<n;i++)

	{

		for(int  j=0;j<n;j++) cin>>ara[i][j];

	}

	for(int mask=0;mask<(1<<n);mask++)

	{

		for(int  j=0;j<n;j++)

		{

			if(mask&(1<<j))

			{

				for(int  k=j+1;k<n;k++)

				{

					if(mask&(1<<k))

					{

						pre[mask]+=ara[j][k]; 

					}

				}

			}

		}

	}

	

	for(int mask=0;mask<(1<<n);mask++)

	{

		dp[mask]=pre[mask]; 

		for(int i=mask;i;i=(i-1)&mask)

		{

			dp[mask]=max(dp[mask],dp[mask^i]+pre[i]);

		}

	}

	cout<<dp[(1LL<<n)-1]<<endl;

}


// Fastest known solution for this problem:
#include <iostream>

#include <algorithm>

#include <vector>

#include <cassert>



using namespace std;





int main() {



  int N;



  cin >> N;

  vector< vector<long long> > A(N, vector<long long>(N));



  for (auto i=0;i <N; i++)

  for (auto j=0;j <N; j++) cin >> A[i][j];



  vector<long long> F( 1<<17, 0);



  // consider all sub sets of {1..N}

  vector<int> l(16, 0);

  for (long long s = 1; s < (1<<N); s++) {

    l.resize(0);



    auto ss= s;

    int c = 0;

    while(ss != 0) {

      if (ss & 1 ) l.push_back(c);

      ss = ss >> 1;

      c++;

    }



    // for (auto tt=0; tt<l.size(); tt++) 

    // cout << "l" << l[tt] << "  ";

    // cout << endl;

    F[s] = 0;

    for (auto i=0; i < l.size(); i++)

    for (auto j=i+1; j < l.size(); j++) F[s] += A[l[i]][l[j]];



    auto L = l.size();



    for (int j = s; j; j = (j-1)&s) {

      F[s] = max( F[s], F[s^j]+F[j]);

    }



    // cout << endl;

  }





  cout << F[ (1<<N)-1] << endl;



  return 0;

}