#include <bits/stdc++.h>
using namespace std;
const int MAXN = 3005;
const int MAXE = 10005;
const int MAXM = 2000005;
struct Line {
  int u, v, val;
  int flag;
  Line() {}
  Line(int u, int v, int val) : u(u), v(v), val(val), flag(0) {}
  bool operator<(const Line& a) const { return val < a.val; }
} line[MAXM];
struct Edge {
  int v;
  Edge* next;
} E[MAXE], *H[MAXN], *edge;
long long sum[MAXN << 2];
int G[MAXN][MAXN];
int siz[MAXN];
int pos[MAXN];
int pre[MAXN];
int top[MAXN];
int son[MAXN];
int val[MAXN];
int dep[MAXN];
int tree_idx;
int p[MAXN];
int cnt;
int n;
void clear() {
  cnt = 0;
  edge = E;
  memset(H, 0, sizeof H);
  tree_idx = 0;
  siz[0] = 0;
  for (int i = 0; i < MAXN; ++i) p[i] = i;
}
void addedge(int u, int v) {
  edge->v = v;
  edge->next = H[u];
  H[u] = edge++;
}
int find(int x) { return p[x] == x ? x : (p[x] = find(p[x])); }
bool kruskal() {
  int count = 1;
  sort(line, line + cnt);
  for (int i = 0; i < cnt; ++i) {
    int x = find(line[i].u);
    int y = find(line[i].v);
    if (x != y) {
      p[x] = y;
      line[i].flag = 1;
      addedge(line[i].u, line[i].v);
      addedge(line[i].v, line[i].u);
      if (++count == n) return 1;
    }
  }
  return 0;
}
void dfs(int u) {
  siz[u] = 1;
  son[u] = 0;
  for (Edge* e = H[u]; e; e = e->next) {
    int v = e->v;
    if (v != pre[u]) {
      pre[v] = u;
      dep[v] = dep[u] + 1;
      dfs(v);
      siz[u] += siz[v];
      if (siz[v] > siz[son[u]]) son[u] = v;
    }
  }
}
void rewrite(int u, int top_element) {
  top[u] = top_element;
  pos[u] = ++tree_idx;
  if (son[u]) rewrite(son[u], top_element);
  for (Edge* e = H[u]; e; e = e->next) {
    int v = e->v;
    if (v != pre[u] && v != son[u]) rewrite(v, v);
  }
}
void pushup(int o) { sum[o] = sum[(o << 1)] + sum[(o << 1 | 1)]; }
void build(int o, int l, int r) {
  if (l == r) {
    sum[o] = val[l];
    return;
  }
  int m = ((l + r) >> 1);
  build((o << 1), l, m);
  build((o << 1 | 1), m + 1, r);
  pushup(o);
}
long long sub_query(int L, int R, int o, int l, int r) {
  if (L <= l && r <= R) return sum[o];
  int m = ((l + r) >> 1);
  if (R <= m) return sub_query(L, R, (o << 1), l, m);
  if (m < L) return sub_query(L, R, (o << 1 | 1), m + 1, r);
  return sub_query(L, R, (o << 1), l, m) +
         sub_query(L, R, (o << 1 | 1), m + 1, r);
}
long long query(int x, int y) {
  long long ans = 0;
  while (top[x] != top[y]) {
    if (dep[top[x]] < dep[top[y]]) swap(x, y);
    ans += sub_query(pos[top[x]], pos[x], 1, 1, n);
    x = pre[top[x]];
  }
  if (x == y) return ans;
  if (dep[x] > dep[y]) swap(x, y);
  ans += sub_query(pos[x] + 1, pos[y], 1, 1, n);
  return ans;
}
void solve() {
  clear();
  bool ok = 1;
  for (int i = 1; i <= n; ++i) {
    for (int j = 1; j <= n; ++j) {
      scanf("%d", &G[i][j]);
      if (i == j && G[i][j]) ok = 0;
      if (i > j && G[i][j] != G[j][i]) ok = 0;
      if (!ok) continue;
      if (i < j && G[i][j]) line[cnt++] = Line(i, j, G[i][j]);
    }
  }
  if (!ok) {
    printf("NO\n");
    return;
  }
  if (n == 1) {
    printf("YES\n");
    return;
  }
  if (!kruskal()) {
    printf("NO\n");
    return;
  }
  dfs(1);
  rewrite(1, 1);
  for (int i = 0; i < cnt; ++i) {
    if (line[i].flag) {
      int x = line[i].u;
      int y = line[i].v;
      if (dep[x] > dep[y])
        val[pos[x]] = line[i].val;
      else
        val[pos[y]] = line[i].val;
    }
  }
  build(1, 1, n);
  for (int i = 1; i <= n; ++i)
    for (int j = i + 1; j <= n; ++j) {
      if (G[i][j] != query(i, j)) ok = 0;
      if (ok == 0) break;
    }
  if (ok)
    printf("YES\n");
  else
    printf("NO\n");
}
int main() {
  while (~scanf("%d", &n)) solve();
  return 0;
}
