import std.stdio, std.conv, std.string, std.array, std.math, std.regex, std.range, std.ascii, std.numeric;
import std.typecons, std.functional, std.traits;
import std.algorithm, std.container;
import core.stdc.stdlib, core.bitop;

struct Node{
    Connect[] connects;
}
struct Connect{
    long a;
    long b;
    long cost;
}
void main()
{
    auto N = scanElem;
    auto grid = new Grid!long(N, N);
    Connect[] costList;

    foreach(y; 0..N)
    foreach(x; 0..N)
    {
        auto v = scanElem;
        if(x>y){
            grid[x, y] = v;
            costList ~= Connect(x, y, v);
        }else{
            grid[x, y] = -1;
        }
    }
    costList.sort!"a.cost<b.cost";

    bool[] used;
    used.length = N;
    long t;
    bool directConnect(long to, long cost, long goal)
    {
        if(cost<0) return true;
        if(used[to]) return true;
        used[to] = true;
        println(to);
        if(to==goal){
            if(cost>0){
                writeln(-1);
                exit(0);
            }
            used[to] = false;
            return false;
        }
        print("search: ", to);
        print(" cost: ", cost);
        println();
        bool isDirect = true;
        foreach(i; 0..to)
        {
            auto c = grid[i, to];
            if(c==-1)continue;
            isDirect &= directConnect(i, cost-c, goal);
        }
        foreach(i; to..N)
        {
            auto c = grid[to, i];
            if(c==-1)continue;
            isDirect &= directConnect(i, cost-c, goal);
        }
        used[to] = false;
        return isDirect;
    }
    foreach(node; costList)
    {
        println(node);
        if(directConnect(node.a, node.cost, node.b)){
            grid[node.b, node.a] = node.cost;
        }
        println(grid);
    }
    long res;
    foreach(y;0..N)
    foreach(x;0..y)
    {
        if(grid[x,y]==-1)continue;
        res += grid[x, y];
    }
    println(grid);
    writeln(res);
}

enum MOD=10^^9+7L;

struct ModInt{
    private long n;

    this(long v)
    {
        n = v%MOD;
    }

    ModInt opBinary(string op, T)(T v)
    if((op=="+"||op=="-"||op=="*")&&(is(T==ModInt)||isIntegral!T))
    {
        return ModInt(mixin("n"~op~"cast(long)v"));
    }

    void opAssign(T)(T v)
    if(is(T==ModInt)||isIntegral!T)
    {
        n = cast(long)v;
    }

    void opOpAssign(string op, T)(T v)
    {
        this = mixin("this"~op~"v");
    }

    long opCast() const {
        return n;
    }

    string toString() const {
        return n.to!string;
    }
}

class GridIter(T){
    private const T[] grid;
    private size_t count;

    this(T[] g)
    {
        grid = g;
    }

    void popFront(){
        count++;
    }
    bool empty(){
        return count>=grid.length;
    }
    T front(){
        return grid[count];
    }
}
class Grid(T){
    private T[] grid;
    size_t width, height;

    private this(){}

    this(size_t w, size_t h)
    {
        width = w;
        height = h;
        grid.length = width*height;
    }

    GridIter!T iter(){
        return new GridIter!T(grid);
    }

    ref T opIndex(size_t x, size_t y)
    {
        return grid[x+width*y];
    }
    ref T opIndex(E)(E e)
        if(
            isIntegral!(typeof(e.x)) &&
            isIntegral!(typeof(e.y)))
    {
        return this[e.x, e.y];
    }

    Grid!T opIndex(size_t[2] r1, size_t[2] r2)
    {
        auto res = new Grid!T();
        auto startOffset = r1[0] + r2[0]*width;
        auto endOffset = r1[1] + (r2[1] - 1)*width;
        res.grid = grid[startOffset .. endOffset];

        res.width = r1[1] - r1[0];
        res.height = r2[1] - r2[0];
        return res;
    }
    
    Grid!T opIndex(size_t[2] r1, size_t j) { return opIndex(r1, [j, j+1]); }
    Grid!T opIndex(size_t i, size_t[2] r2) { return opIndex([i, i+1], r2); }

    size_t[2] opSlice(size_t dim)(size_t start, size_t end)
    if (dim == 0 || dim == 1)
    in { assert(start >= 0 && end <= this.opDollar!dim); }
    body{
        return [start, end];
    }

    size_t opDollar(size_t dim : 0)() { return width; }
    size_t opDollar(size_t dim : 1)() { return height; }

    override string toString() const {
        string res = "\n";
        foreach(line; 0..height)
        {
            res ~= grid[line*width..(line+1)*width].to!string ~ '\n';
        }
        return res;
    }
}

struct Data2{
    long x,y;
    alias a=x, b=y;
    ref long opIndex(size_t i)
    {
        assert(i==0||i==1);
        return i==0?x:y;
    }
}
struct Data3{
    long x,y,z;
    alias a=x, b=y, c=z;
    ref long opIndex(size_t i)
    {
        assert(i==0||i==1||i==2);
        return i==0?x:(i==1?y:z);
    }
}

long lcm(long a, long b) pure
{
    return a * b / gcd(a, b);
}

class UnionFind{
    UnionFind parent = null;

    void merge(UnionFind a)
    {
        if(same(a)) return;
        a.root.parent = this.root;
    }
    UnionFind root()
    {
        if(parent is null)return this;
        return parent = parent.root;
    }
    bool same(UnionFind a)
    {
        return this.root == a.root;
    }
}

enum TermColor{
    Blue,
    Red,
    Purple,
    Gray,
    Green,
    Brown,
    Default,
}

string termStr(TermColor c) pure
{
    switch(c)
    {
        case TermColor.Blue:   return "\033[38;2;122;158;194m";
        case TermColor.Red:    return "\033[38;2;204;130;66m";
        case TermColor.Purple: return "\033[38;2;158;123;176m";
        case TermColor.Gray:   return "\033[38;2;106;135;89m";
        case TermColor.Green:  return "\033[38;2;165;194;97m";
        case TermColor.Brown:  return "\033[38;2;204;130;66m";
        default: return "\033[0m";
    }
}

void print(TermColor C = TermColor.Gray, T...)(T t)
{
    debug stderr.write(C.termStr, t, TermColor.Default.termStr);
}
void printf(TermColor C = TermColor.Gray, T...)(T t)
{
    debug stderr.write(C.termStr);
    debug stderr.writef(t);
    debug stderr.write(TermColor.Default.termStr);
}
void println(TermColor C = TermColor.Gray, T...)(T t)
{
    debug stderr.writeln(C.termStr, t, TermColor.Default.termStr);
}
void printfln(TermColor C = TermColor.Gray, T...)(T t)
{
    debug stderr.write(C.termStr);
    debug stderr.writefln(t);
    debug stderr.write(TermColor.Default.termStr);
}

// debug{
//     void write(T...)(T t)
//     {
//         stdout.write(TermColor.Blue.termStr, t, TermColor.Default.termStr);
//     }
//     void writeln(T...)(T t)
//     {
//         stdout.writeln(TermColor.Blue.termStr, t, TermColor.Default.termStr);
//     }
//     void writefln(T...)(T t)
//     {
//         stdout.writefln(TermColor.Blue.termStr, t, TermColor.Default.termStr);
//     }
// }

T[] scanArray(T = long)()
{
    return readln.split.to!(T[]);
}

T scanElem(T = long)()
{
    char[] res;
    int c = ' ';
    while (isWhite(c) && c != -1)
    {
        c = getchar;
    }
    while (!isWhite(c) && c != -1)
    {
        res ~= cast(char) c;
        c = getchar;
    }
    return res.strip.to!T;
}

alias scanString = scanElem!string;

template fold(fun...) if (fun.length >= 1)
{
    auto fold(R, S...)(R r, S seed)
    {
        static if (S.length < 2)
        {
            return reduce!fun(seed, r);
        }
        else
        {
            import std.typecons : tuple;

            return reduce!fun(tuple(seed), r);
        }
    }
}

struct Factor
{
    long n;
    long c;
}

//素因数分解
Factor[] factors(long n) pure
{
    Factor[] res;
    for (long i = 2; i ^^ 2 <= n; i++)
    {
        if (n % i != 0)
            continue;

        long c;
        while (n % i == 0)
        {
            n = n / i;
            c++;
        }
        res ~= Factor(i, c);
    }
    if (n != 1)
        res ~= Factor(n, 1);

    return res;
}
//約数をすべて列挙
long[] divisors(long n) pure
{
    long[] list;
    void func(Factor[] fs, long n)
    {
        if(fs.empty){
            list ~= n;
            return;
        }

        foreach(c; 0..fs[0].c+1)
        {
            func(fs[1..$], n * (fs[0].n ^^ c));
        }
    }

    func(factors(n), 1);
    sort(list);
    return list;
}
//nまでの素数のリスト
long[] primes(long n) pure
{
    if(n<2)return [];
    auto table = new long[n+1];
    long[] res;
    for(long i = 2;i<=n;i++)
    {
        if(table[i]==-1) continue;
        for(long a = i;a<table.length;a+=i)
        {
            table[a] = -1;
        }
        res ~= i;
    }
    return res;
}
//素数判定
bool isPrime(long n) pure
{
    if (n <= 1)
        return false;
    if (n == 2)
        return true;
    if (n % 2 == 0)
        return false;
    for (long i = 3; i ^^ 2 <= n; i += 2)
        if (n % i == 0)
            return false;
    return true;
}