#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=a;i<b;i++)
#define rrep(i,a,b) for(int i=a;i>=b;i--)
#define fore(i,a) for(auto &i:a)
#define all(x) (x).begin(),(x).end()
//#pragma GCC optimize ("-O3")
using namespace std; void _main(); int main() { cin.tie(0); ios::sync_with_stdio(false); _main(); }
typedef long long ll; const int inf = INT_MAX / 2; const ll infl = 1LL << 60;
template<class T>bool chmax(T &a, const T &b) { if (a<b) { a = b; return 1; } return 0; }
template<class T>bool chmin(T &a, const T &b) { if (b<a) { a = b; return 1; } return 0; }
//------------------------------------------------------------------------------
class UnionFind
{
public:
    UnionFind(int size) : m_Nodes(size)
    {
        Init();
    }
 
    void Init()
    {
        for (int i = 0, size = m_Nodes.size(); i < size; ++i)
        {
            m_Nodes[i].Init(i);
        }
    }
 
    int Root(int x)
    {
        const int parent = m_Nodes[x].m_Parent;
        if (parent == x)
        {
            return x;
        }
        else
        {
            return m_Nodes[x].m_Parent = Root(parent);
        }
    }
 
    bool Same(int x, int y)
    {
        return Root(x) == Root(y);
    }
 
    bool Unite(int x, int y)
    {
        x = Root(x);
        y = Root(y);
 
        if (x == y) return false;
 
        if (m_Nodes[x].m_Rank < m_Nodes[y].m_Rank)
        {
            m_Nodes[x].m_Parent = y;
            m_Nodes[y].m_Size += m_Nodes[x].m_Size;
        }
        else
        {
            m_Nodes[y].m_Parent = x;
            m_Nodes[x].m_Size += m_Nodes[y].m_Size;
            if (m_Nodes[x].m_Rank == m_Nodes[y].m_Rank) ++m_Nodes[x].m_Rank;
        }
 
        return true;
    }
 
    int Size(int x)
    {
        return m_Nodes[Root(x)].m_Size;
    }
 
private:
    struct Node
    {
    public:
        void Init(int index)
        {
            m_Parent = index;
            m_Rank = 0;
            m_Size = 1;
        }
 
        int m_Parent = -1;
        int m_Rank = -1;
        int m_Size = -1;
    };
 
    vector<Node> m_Nodes;
};
//------------------------------------------------------------------------------
void _main()
{
    int N;
    cin >> N;
    vector<vector<ll>> A(N, vector<ll>(N));
    rep(i, 0, N) rep(j, 0, N) cin >> A[i][j];
    vector<tuple<ll, int, int>> v;
    rep(i, 0, N)
    {
        rep(j, i + 1, N)
        {
            v.push_back(make_tuple(A[i][j], i, j));
        }
    }
    sort(v.begin(), v.end());

    UnionFind uf(N);

    ll distSum = 0;

    vector<vector<ll>> B(N, vector<ll>(N));
    rep(i, 0, N) rep(j, 0, N)
    {
        if (i == j) B[i][j] = 0;
        else B[i][j] = 100000000000000;
    }

    for (const auto& t : v)
    {
        ll dist;
        int i, j;
        tie(dist, i, j) = t;

        if (uf.Same(i, j))
        {
            if (B[i][j] < dist)
            {
                cout << -1 << endl;
                return;
            }
            else if (B[i][j] > dist)
            {
                distSum += dist;
                B[i][j] = B[j][i] = dist;
            }
        }
        else
        {
            uf.Unite(i, j);
            
            distSum += dist;

            B[i][j] = B[j][i] = dist;
        }

        rep(k, 0, N)
        {
            if (i == k || j == k) continue;

            if (uf.Same(i, k))
            {
                if (chmin(B[i][k], dist + B[j][k]))
                {
                    B[k][i] = B[i][k];
                }
            }

            if (uf.Same(j, k))
            {
                if (chmin(B[j][k], dist + B[i][k]))
                {
                    B[k][j] = B[j][k];
                }
            }
        }
    }

    cout << distSum << endl;
}


Measured memory_bytes: 2428