#include <bits/stdc++.h>

#define TYPE(c)   remove_reference_t<decltype(c)>
#define REP(i, n) for(TYPE(n) i = 0; i < n; i++)
#define FOR(v, c) for(TYPE(c.begin()) v = c.begin(); v != c.end(); v++)
#define ALL(c)    c.begin(), c.end()
#define SORT(c)   sort(ALL(c))
#define RSORT(c)  sort(ALL(c), greater<TYPE(c)::value_type>())
#define UNIQUE(c) c.erase(unique(ALL(c)), c.end())

using namespace std;
using ll = long long;

constexpr int MOD  = (int)1e9 + 7;
constexpr int INF  = (int)1e9 + 1;
constexpr ll  LINF = (ll)1e18 + 1;
template<typename T> constexpr bool chmax(T& a, const T& b)
{ if(a < b) { a = b; return true; } else { return false; } }
template<typename T> constexpr bool chmin(T& a, const T& b)
{ if(b < a) { a = b; return true; } else { return false; } }
template<typename S, typename T> ostream& operator<<(ostream &os, const pair<S, T>& p)
{ return os << "(" << p.first << "," << p.second << ")"; }
template<typename T> auto operator<<(ostream& os, const T& t)
    -> typename conditional<true, ostream&, decltype(*t.begin())>::type
{ os << "["; FOR(v, t) { if(v != t.begin()) os << ","; os << *v; } os << "]"; return os; }

class UnionFind {
    using ll = long long;
public:
    struct Node {
        ll parent, weight, size, rank;
        Node(const ll& _parent = 0,
             const ll& _weight = 0,
             const ll& _size   = 1,
             const ll& _rank   = 0) :
            parent(_parent),
            weight(_weight),
            size(_size),
            rank(_rank) { }
    };

    explicit UnionFind(const ll& num_node) {
        node_.reserve(num_node);
        for(ll i = 0; i < num_node; i++) {
            node_.emplace_back(i);
        }
    }

    const std::vector<Node>& getNode() const {
        return node_;
    }

    ll searchAndZipRoot(const ll& x) {
        if(node_[x].parent == x) {
            return x;
        }
        else {
            node_[x].weight += node_[node_[x].parent].weight;
            return node_[x].parent = searchAndZipRoot(node_[x].parent);
        }
    }

    void unite(const ll& x,
               const ll& y,
               const ll& weight = 0) {
        auto rx = searchAndZipRoot(x);
        auto ry = searchAndZipRoot(y);
        if(rx != ry) {
            auto yw = weight + calcWeight(x) - calcWeight(y);
            if(node_[rx].rank < node_[ry].rank) {
                std::swap(rx, ry);
                yw *= -1;
            }
            node_[ry].parent  = rx;
            node_[ry].weight  = yw;
            node_[rx].size   += node_[ry].size;
            if(node_[rx].rank == node_[ry].rank) {
                node_[rx].rank++;
            }
        }
    }

    ll calcWeight(const ll& x) {
        searchAndZipRoot(x);
        return node_[x].weight;
    }

    ll calcDiff(const ll& x,
                const ll& y) {
        return std::abs(calcWeight(x) - calcWeight(y));
    }

    ll calcSize(const ll& x) {
        return node_[searchAndZipRoot(x)].size;
    }

    template<class... A>
    bool checkWhetherRootIsSame(const A&... nodes) {
        auto node_list = std::initializer_list<ll>{nodes...};
        if(node_list.size() < 2) return false;
        auto v = searchAndZipRoot(*node_list.begin());
        for(size_t i = 1; i < node_list.size(); i++) {
            if(v != searchAndZipRoot(*(node_list.begin() + i))) return false;
        }
        return true;
    }

private:
    std::vector<Node> node_;
};

class CommonGraph {
    using ll = long long;
public:
    struct Edge {
        enum class Type { Directed, Undirected };
        int    src, dst;
        ll     cost, capacity, flow;
        size_t rev;
        Edge() = default;
        Edge(const int&    _src,
             const int&    _dst,
             const ll&     _cost     = 0,
             const ll&     _capacity = 0,
             const ll&     _frow     = 0,
             const size_t& _rev      = 0) :
            src(_src),
            dst(_dst),
            cost(_cost),
            capacity(_capacity),
            flow(_frow),
            rev(_rev) { }
    };
    friend std::ostream& operator <<(std::ostream& os, const Edge& obj) {
        os << "src : "  << obj.src      << ", "
           << "dst : "  << obj.dst      << ", "
           << "cost : " << obj.cost     << ", "
           << "cap : "  << obj.capacity << ", "
           << "flow : " << obj.flow     << ", "
           << "rev : "  << obj.rev;
        return os;
    }

    struct Vertex {
        ll cost;
        Vertex(const ll& _cost) : cost(_cost) { }
    };

    enum class Type { Normal, Flow };

    CommonGraph(const int&        num_vertex,
                const Type&       type,
                const Edge::Type& edge_type) :
        vertex_(num_vertex, 0),
        edge_(num_vertex),
        num_edge_(0),
        total_cost_(0),
        type_(type),
        edge_type_(edge_type) { }

    ll getTotalCost() const {
        return total_cost_;
    }

    const std::vector<Vertex>& getVertex() const {
        return vertex_;
    }

    const std::vector<std::vector<Edge>>& getEdge() const {
        return edge_;
    }

    /**
     *  辺を追加する
     */
    void addEdge(const int& src,
                 const int& dst,
                 const ll&  cost,
                 const ll&  capacity = 0) {
        switch(type_) {
            case Type::Normal: {
                switch(edge_type_) {
                    case Edge::Type::Directed: {
                        edge_[src].emplace_back(src, dst, cost);
                        break;
                    }
                    case Edge::Type::Undirected: {
                        edge_[src].emplace_back(src, dst, cost);
                        edge_[dst].emplace_back(dst, src, cost);
                        break;
                    }
                }
                break;
            }
            case Type::Flow: {
                switch(edge_type_) {
                    case Edge::Type::Directed: {
                        edge_[src].emplace_back(src, dst, cost,  capacity, 0, edge_[dst].size());
                        edge_[dst].emplace_back(dst, src, -cost,        0, 0, edge_[src].size() - 1);
                        break;
                    }
                    case Edge::Type::Undirected: {
                        edge_[src].emplace_back(src, dst, cost, capacity, 0, edge_[dst].size());
                        edge_[dst].emplace_back(dst, src, cost, capacity, 0, edge_[src].size() - 1);
                        break;
                    }
                }
                break;
            }
        }
        num_edge_++;
        total_cost_ += cost;
    }

    /**
     *  辺の情報を標準出力する
     */
    void dumpEdgeInfo() {
        for(const auto& v : edge_) {
            for(const auto& e : v) {
                std::cout << "[" << e << "]" << std::endl;
            }
            std::cout << std::endl;
        }
    }

    /**
     *  最大流問題を解く(Dinic法)
     *  @src_index:  始点
     *  @sink_index: 終点
     *  @Return:     最大流
     */
    ll calcMaxFlow(const int& src_index,
                   const int& sink_index) {
        if(type_ != Type::Flow) {
            throw std::logic_error("Graph type is invalid");
        }
        std::vector<int> level(vertex_.size()), iter(vertex_.size());
        std::function<int(void)> levelize
            = [&]() {
                  level.assign(vertex_.size(), -1);
                  level[src_index] = 0;
                  queue<int> Q; Q.push(src_index);
                  while(!Q.empty()) {
                      int u = Q.front(); Q.pop();
                      if(u == sink_index) break;
                      for(const auto& e : edge_[u]) {
                          if(e.flow < e.capacity && level[e.dst] < 0) {
                              Q.push(e.dst);
                              level[e.dst] = level[u] + 1;
                          }
                      }
                  }
                  return level[sink_index];
              };
        function<ll(int, ll)> augment
            = [&](int u, ll cur) {
                  if(u == sink_index) return cur;
                  for(size_t i = iter[u]; i < edge_[u].size(); ++i) {
                      auto& e = edge_[u][i];
                      auto& r = edge_[e.dst][e.rev];
                      if(e.flow < e.capacity && level[u] < level[e.dst]) {
                          auto f = augment(e.dst, min(cur, e.capacity - e.flow));
                          if(0 < f) { e.flow += f; r.flow -= f; return f; }
                      }
                  }
                  return 0LL;
              };
        for(auto& v : edge_) for(auto& e : v) e.flow = 0;
        auto flow = 0LL;
        while(0 <= levelize()) {
            std::fill(iter.begin(), iter.end(), 0);
            for(ll f; 0 < (f = augment(src_index, INF)); flow += f);
        }
        return flow;
    }

    /**
     *  木において、すべての頂点に対してある頂点からの距離を求める
     *  @table: std::fill(ALL(table) -1); table[start_inded] = 0;
     *  @index: start_index
     *  @Return: void
     */
    void calcCostForTreeByDFS(vector<int>& table,
                              int index) const {
        if(type_ != Type::Normal || edge_type_ != Edge::Type::Undirected) {
            throw std::logic_error("Graph type is invalid");
        }
        for(const auto& edge : edge_[index]) {
            if(table[edge.dst] == -1) {
                table[edge.dst] = table[edge.src] + edge.cost;
                calcCostForTreeByDFS(table, edge.dst);
            }
        }
    }

    /**
     *  頂点間の最小コストを求める(BFS)
     *  @min_cost:    最小コスト
     *  @start_index: 始点
     *  @goal_index:  終点
     *  @Return:      始点-終点が非連結の場合falseを返す
     */
    bool calcMinCostByBFS(ll&        min_cost,
                          const int& start_index,
                          const int& goal_index) {
        if(type_ != Type::Normal) {
            throw std::logic_error("Graph type is invalid");
        }
        std::fill(vertex_.begin(), vertex_.end(), std::numeric_limits<ll>::max());
        vertex_[start_index] = 0;
        queue<int> Q; Q.push(start_index);
        while(!Q.empty()) {
            const auto src = Q.front(); Q.pop();
            for(const auto& v : edge_[src]) {
                const auto& dst  = v.dst;
                const auto  cost = vertex_[src].cost + v.cost;
                if(cost < vertex_[dst].cost) {
                    vertex_[dst] = cost; Q.push(dst);
                }
            }
        }
        min_cost = vertex_[goal_index].cost;
        return (min_cost == std::numeric_limits<ll>::max()) ? false : true;
    }

    /**
     *  最小全域木を求める(Kruskal法)
     *  @Return: 最小全域木を返す
     *           グラフが非連結の場合、連結成分ごとに最小全域木を構成する
     */
    CommonGraph generateMinimumSpanningTree() {
        if(type_ != Type::Normal || edge_type_ != Edge::Type::Undirected) {
            throw std::logic_error("Graph type is invalid");
        }
        auto num_v = vertex_.size();
        CommonGraph mst(num_v, Type::Normal, Edge::Type::Undirected);
        std::vector<Edge> mst_e;
        mst_e.reserve(num_edge_ * 2);
        for(const auto& v : edge_) for(const auto& e : v) mst_e.push_back(e);
        auto sort_rule = [](const Edge& l, const Edge& r) { return l.cost < r.cost; };
        sort(mst_e.begin(), mst_e.end(), sort_rule);
        UnionFind uf(num_v);
        for(const auto& e : mst_e) {
            if(!uf.checkWhetherRootIsSame(e.src, e.dst)) {
                mst.addEdge(e.src, e.dst, e.cost);
                uf.unite(e.src, e.dst);
            }
        }
        return mst;
    }

    /**
     *  全頂点間の最短距離を求める(Warshall-Floyd法)
     *  @Return: array[i][j] :: 頂点iから頂点jまでの最短距離
     */
    std::vector<std::vector<ll>> calcMinCostBetweenAllVertices() {
        if(type_ != Type::Normal) {
            throw std::logic_error("Graph type is invalid");
        }
        std::vector<std::vector<ll>> dp(vertex_.size(),
                                        std::vector<ll>(vertex_.size(),
                                                        std::numeric_limits<uint>::max()));
        for(const auto& v : edge_) for(const auto& e : v) dp[e.src][e.dst] = e.cost;
        for(size_t i = 0; i < vertex_.size(); i++)
            for(size_t j = 0; j < vertex_.size(); j++)
                for(size_t k = 0; k < vertex_.size(); k++)
                    dp[j][k] = std::min(dp[j][k], dp[j][i] + dp[i][k]);
        return dp;
    }

private:
    std::vector<Vertex>            vertex_;
    std::vector<std::vector<Edge>> edge_;
    ll                             num_edge_;
    ll                             total_cost_;
    const Type                     type_;
    const Edge::Type               edge_type_;
};

int main() {
    cin.tie(0);
    ios::sync_with_stdio(false);

    ll N;
    std::cin >> N;

    CommonGraph g(N,
                  CommonGraph::Type::Normal,
                  CommonGraph::Edge::Type::Undirected);

    vector<vector<ll>> A(N, vector<ll>(N, 0));
    REP(i, N) {
        REP(j, N) {
            std::cin >> A[i][j];
            if(i <= j) continue;
            g.addEdge(i, j, A[i][j]);
        }
    }

    auto dp = g.calcMinCostBetweenAllVertices();

    ll ans = 0;
    [&]() {
        REP(i, N) {
            REP(j, N) {
                if(i <= j) continue;
                if(dp[i][j] < A[i][j]) {
                    ans = -1;
                    return;
                }
                bool direct = true;
                REP(k, N) {
                    if(k == i || k == j) continue;
                    if(dp[i][j] == A[i][k] + A[k][j]) {
                        direct = false;
                        break;
                    }
                }
                if(direct) ans += dp[i][j];
            }
        }
    }();

    std::cout << ans << std::endl;

    return 0;
}


Measured memory_bytes: 6912