#include <cstdio>
#include <iostream>
#include <algorithm>
#include <vector>
#include <set>
#include <map>
#include <string>
#include <cstring>
#include <stack>
#include <queue>
#include <cmath>
#include <ctime>
#include <bitset>
#include <utility>
#include <assert.h>
using namespace std;
#define rank rankk
#define mp make_pair
#define pb push_back
#define xo(a,b) ((b)&1?(a):0)
#define tm tmp
//#define LL ll
typedef unsigned long long ull;
typedef pair<int,int> pii;
typedef long long ll;
typedef pair<ll,int> pli;
typedef pair<ll,ll> pll;
const int INF=0x3f3f3f3f;
const ll INFF=0x3f3f3f3f3f3f3f3fll;
const int MAX=1e5+5;
//const ll MAXN=2e8;
const int MAX_N=MAX;
const ll MOD=1e9+7;
//const long double pi=acos(-1.0);
//const double eps=0.00000001;
int gcd(int a,int b){return b?gcd(b,a%b):a;}
template<typename T>inline T abs(T a) {return a>0?a:-a;}
template<class T> inline
void read(T& num) {
    bool start=false,neg=false;
    char c;
    num=0;
    while((c=getchar())!=EOF) {
        if(c=='-') start=neg=true;
        else if(c>='0' && c<='9') {
            start=true;
            num=num*10+c-'0';
        } else if(start) break;
    }
    if(neg) num=-num;
}
inline ll powMM(ll a,ll b,ll M){
    ll ret=1;
    a%=M;
//    b%=M;
    while (b){
        if (b&1) ret=ret*a%M;
        b>>=1;
        a=a*a%M;
    }
    return ret;
}
void open()
{
//    freopen("1009.in","r",stdin);
    freopen("out.txt","w",stdout);
}
int n;
int dis[303][303];
bool used[303][303];
bool ad[303][303];
int par[MAX];//父亲
int rank[MAX];//树的高度
//初始化n个元素
void init(int n)
{
    for(int i=0;i<n;i++)
    {
        par[i]=i;
        rank[i]=0;
    }
}
//查询树的根，期间加入了路径压缩
int find(int x)
{
    if(par[x]==x)
        return x;
    else
        return par[x]=find(par[x]);
}
//合并x和y所属的集合
void unite(int x,int y)
{
    x=find(x);
    y=find(y);
    if(x==y)
        return ;
    if(rank[x]<rank[y])
        par[x]=y;
    else
    {
        par[y]=x;
        if(rank[x]==rank[y])
            rank[x]++;
    }
}
//判断x和y是否属于同一个集合
bool same(int x,int y)
{
    return find(x)==find(y);
}
/*
建立结构体记录边
*/
struct edge
{
    int u,v,cost;
};
/*储存边的数组*/
edge es[MAX];
int V,E;//顶点数、边数
bool cmp(const edge &e1,const edge &e2)
{
    return e1.cost<e2.cost;
}
ll kruskal(edge *e,int edge_num,int vertice_num)//e为存储边的数组，下标从0开始
{
    sort(e,e+edge_num,cmp);//按照edge.cost顺序从小到大排序
    init(vertice_num);//初始化并查集
    ll re=0;
    for(int i=0;i<edge_num;i++)
    {
        edge tem=e[i];
        if(!same(tem.u,tem.v))
        {
            unite(tem.u,tem.v);
            ad[tem.u][tem.v]=true;
            re+=(ll)tem.cost;
        }
    }
    return re;
}


int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
        scanf("%d",&dis[i][j]);
    for(int i=1;i<=n;i++)
        for(int j=i+1;j<=n;j++)
            {
                for(int s=1;s<=n;s++)
            {
                if(s==i||s==j)continue;
                if(dis[i][s]+dis[s][j]<dis[i][j])return 0*printf("-1\n");
                else if(dis[i][s]+dis[s][j]==dis[i][j])
                    used[i][j]=true;//可以不要
            }
            }
    int cnt=0;
    for(int i=1;i<=n;i++)
        for(int j=i+1;j<=n;j++)
            es[cnt++]=edge{i,j,dis[i][j]};
    ll an=kruskal(es,cnt,n);
    for(int i=1;i<=n;i++)
        for(int j=i+1;j<=n;j++)
        if(!used[i][j]&&!ad[i][j])
            an+=dis[i][j];
    printf("%lld\n",an);
    return 0;
}

/*
1000000000000000000
*/


Measured memory_bytes: 1280