#include <bits/stdc++.h>
using namespace std;
struct Edge {
  int s, t, c;
  Edge(int s, int t, int c) : s(s), t(t), c(c) { ; }
};
struct UnionFind {
  vector<int> data;
  UnionFind(int size) : data(size, -1) {}
  bool unionSet(int x, int y) {
    x = root(x);
    y = root(y);
    if (x != y) {
      if (data[y] < data[x]) swap(x, y);
      data[x] += data[y];
      data[y] = x;
    }
    return x != y;
  }
  bool findSet(int x, int y) { return root(x) == root(y); }
  int root(int x) { return data[x] < 0 ? x : data[x] = root(data[x]); }
  int size(int x) { return -data[root(x)]; }
};
bool operator<(const Edge &e, const Edge &f) {
  return (e.c != f.c) ? e.c > f.c : (e.s != f.s) ? e.s > f.c : e.t > f.t;
}
int solve(vector<vector<Edge> > &g, int hots) {
  const int n = g.size();
  int i, j;
  vector<int> atr(n, 0);
  for (i = 0; i < hots; ++i) atr[i] = 1;
  UnionFind uf(n);
  priority_queue<Edge> Q;
  for (i = 0; i < hots; ++i)
    for (j = 0; j < g[i].size(); ++j) Q.push(g[i][j]);
  int ans = 0;
  while (!Q.empty()) {
    Edge e = Q.top();
    Q.pop();
    if (uf.findSet(e.s, e.t)) continue;
    if (atr[e.s] != 0 && atr[e.t] != 0) continue;
    ans += e.c;
    uf.unionSet(e.s, e.t);
    if (atr[e.s] == 0 && atr[e.t] == 0) assert(false);
    if (atr[e.s] == 0) {
      atr[e.s] = 1;
      for (i = 0; i < g[e.s].size(); ++i) Q.push(g[e.s][i]);
    } else if (atr[e.t] == 0) {
      atr[e.t] = 1;
      for (i = 0; i < g[e.t].size(); ++i) Q.push(g[e.t][i]);
    }
  }
  return ans;
}
int main() {
  int s, d, i, j, k;
  while (cin >> s >> d, s | d) {
    vector<vector<Edge> > g(s + d);
    for (i = 0; i < s; ++i) {
      for (j = 0; j < d; ++j) {
        cin >> k;
        if (!k) continue;
        g[i].push_back(Edge(i, j + s, k));
      }
    }
    for (i = 0; i < d; ++i) {
      for (j = i + 1; j < d; ++j) {
        cin >> k;
        if (!k) continue;
        g[i + s].push_back(Edge(i + s, j + s, k));
      }
    }
    cout << solve(g, s) << endl;
  }
}
