/*
    String[]の初期化、整数の型がlongになっているか
    関数（splitとかcontainsとか）内では"+"とかは正規表現となされるので"\\+"とする
    配列のコピーはarr1=arr.clone()
    HashSet<>[]はおのおの初期化した？？？？？
    負の数の割り算は絶対値が小さいほうに切り捨てられるex.-1/2=0ので、ある値>kみたいな式だとバグらせやすいので注意
    ある値>=kとして、切り上げた値をとるべき
    toLowerCase()はアルファベット以外に対して行ってもエラーにはならない
    ArrayDequeではpopとpushはStackと同じ操作になる
    //import static java.lang.System.*; はしばらく使わなそう(使うとしてもexit(0)のためだけ)←return;でよさそう
    最後にout.flush()またはout.close()をするのを忘れずに！
    Point2D p2d=new Point2D.Double(d1,d2);
    */
    //入力終了→Ctrl+D
    import java.util.*;
    import java.io.*;
    import java.awt.*;
    import java.awt.geom.Point2D;
    import static java.lang.Math.*;
    public class Main {
        public static void main(String[] $) {
            int n=sc.nextInt();
            UF.Init(n);
            int c=0;
            int[][] a=new int[n][n];
            for (int i = 0; i < n; i++) {
                for (int j = 0; j < n; j++) {
                    a[i][j] = sc.nextInt();
                    if (a[i][j] != -1 && i>j) c++;
                }
            }
            Point[] edge=new Point[c];
            int[] cost=new int[c];
            PriorityQueue<Integer> S=new PriorityQueue<>((p1,p2)->cost[p1]-cost[p2]);
            c=0;
            for (int i = 0; i < n; i++) {
                for (int j = i+1; j < n; j++) {
                    if (a[i][j] != -1) {
                        cost[c] = a[i][j];
                        edge[c] = new Point(i, j);
                        c++;
                    }
                }
            }
            for (int i = 0; i < c; i++) {
                S.add(i);
            }
            int ans=0;
            HashSet<Integer> F=new HashSet<>();
            while (!S.isEmpty()){
                int e=S.poll();
                if(!UF.same(edge[e].x,edge[e].y)){
                    ans+=cost[e];
                    UF.unite(edge[e].x,edge[e].y);
                }
            }
            out.println(ans);
            //out.println(Arrays.toString(cost));
            //out.println(Arrays.toString(edge));
            out.close();
        }

        //Union Find木(0-indexed)
        static class UF {
            static int[] d;

            static void Init(int size) {
                d = new int[size];
                for (int i = 0; i < size; i++) {
                    d[i] = -1;
                }
            }

            static boolean unite(int x, int y) {
                x = find(x);
                y = find(y);
                if (x == y) return false;
                if (d[x] > d[y]) {
                    int t = d[y];
                    d[y] = d[x];
                    d[x] = t;
                }
                d[x] += d[y];
                d[y] = x;
                return true;
            }

            static boolean same(int x, int y) {
                return find(x) == find(y);
            }

            static int find(int k) {
                return d[k] < 0 ? k : (d[k] = find(d[k]));
            }

            static int size(int k) {
                return -d[find(k)];
            }
        }

        static PrintWriter out = new PrintWriter(System.out);

        static class sc {
            static Scanner s = new Scanner(System.in);

            static String next() {
                return s.next();
            }

            static String nextLine() {
                return s.nextLine();
            }

            static int nextInt() {
                return Integer.parseInt(s.next());
            }

            static long nextLong() {
                return Long.parseLong(s.next());
            }

            static double nextDouble() {
                return Double.parseDouble(s.next());
            }

            static boolean hasNext() {
                return s.hasNext();
            }

            static boolean hasNextInt() {
                return s.hasNextInt();
            }

        }

    }


Measured memory_bytes: 44960