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

using namespace std;

typedef long long ll;



const int mxN = 17;

int N, a[mxN][mxN];

ll scores[1 << mxN], dp[1 << mxN];

vector<int> v;



void rec(int idx, int mask, int grp, ll score_now)

{

	if (idx == (int)v.size())

	{

		dp[mask] = max(dp[mask], score_now + scores[grp]);

		return;

	}

	rec(idx + 1, mask, grp, score_now);

	rec(idx + 1, mask ^ (1 << v[idx]), grp ^ (1 << v[idx]), score_now);

}



int main()

{

	ios_base::sync_with_stdio(false);



	cin >> N;

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

	{

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

			cin >> a[i][j];

	}

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

	{

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

		{

			for (int j = i + 1; j < N; j++)

			{

				if ((mask & (1 << i)) && (mask & (1 << j)))

					scores[mask] += a[i][j];

			}

		}

	}

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

	dp[0] = 0;

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

	{

		v.clear();

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

		{

			if (!(mask & (1 << i)))

				v.push_back(i);

		}

		rec(0, mask, 0, dp[mask]);

	}

	cout << dp[(1 << N) - 1];



	return 0;

}

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

using namespace std;

typedef long long ll;



const int mxN = 17;

int N, a[mxN][mxN];

ll dp[1 << mxN], scores[1 << mxN];

vector<int> rem;



void rec(int idx, int mask, ll score, int grp) {

	if (idx == (int) rem.size()) {

		dp[mask] = max(dp[mask], score + scores[grp]);

		return;

	}

	rec(idx + 1, mask, score, grp);

	rec(idx + 1, mask ^ (1 << rem[idx]), score, grp ^ (1 << rem[idx]));

}



int main() {

	ios_base::sync_with_stdio(false);

	cin.tie(0);

	

	cin >> N;

	for (int i = 0; i < N; i++) {

		for (int j = 0; j < N; j++) {

			cin >> a[i][j];

		}

	}

	for (int mask = 0; mask < (1 << N); mask++) {

		for (int i = 0; i < N; i++) {

			for (int j = i + 1; j < N; j++) {

				if ((mask & (1 << i)) && (mask & (1 << j))) {

					scores[mask] += a[i][j];

				}

			}

		}

	}

	for (int mask = 0; mask < (1 << N); mask++) {

		rem.clear();

		for (int i = 0; i < N; i++) {

			if (!(mask & (1 << i))) {

				rem.push_back(i);

			}

		}

		rec(0, mask, dp[mask], 0);

	}

	cout << dp[(1 << N) - 1];

	

	return 0;

}


// 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;

}