#include <set>
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <vector>
#include <cstring>

#define REP(i,n) for(int i=0; i<(int)(n); i++)

inline int getInt(){ int s; scanf("%d", &s); return s; }

using namespace std;

int n, m;

int bingo[4][4][4];
int hit[4][4];
int nums[4 * 4];
int ans;

vector<pair<int, pair<int, int> > > memo[100];

bool bingoCheck(int hit[4]){
  // tate
  REP(i,m){
    if(hit[i] == (1 << m) - 1) return true;
  }
  // yoko
  int tmp = (1 << m) - 1;
  REP(i,m){
    tmp &= hit[i];
  }
  if(tmp != 0) return true;

  {
    // naname1
    int cnt = 0;
    REP(i,m) cnt += (hit[i] & (1 << i)) != 0;
    if(cnt == m) return true;
  }
  {
    // naname2
    int cnt = 0;
    REP(i,m) cnt += (hit[i] & (1 << (m - i - 1))) != 0;
    if(cnt == m) return true;
  }
  return false;
}

void bingoGo(int num, bool which){
  REP(c, memo[num].size()){
    int i = memo[num][c].first;
    int j = memo[num][c].second.first;
    int k = memo[num][c].second.second;

    if(which)
      hit[i][j] |= (1 << k);
    else
      hit[i][j] &= ~(1 << k);
  }
}

bool dp[1<<12];
int nn[4 * 4];
int ns;

bool cango(int flag){
  if(flag == (1 << ns) - 1) return true;
  if(dp[flag]) return false;
  dp[flag] = true;

  REP(i,ns) if((flag & (1 << i)) == 0){
    bingoGo(nn[i], true);

    int ff = 0;
    REP(j,n) if(bingoCheck(hit[j])) ff |= (1 << j);

    bool ok = true;
    REP(j,n-1) if((ff & (1 << j)) == 0 && (ff & (1 << (j + 1))) != 0){
      ok = false;
      break;
    }

    if(ok && cango(flag ^ (1 << i)))
      return true;

    bingoGo(nn[i], false);
  }
  return false;
}

bool check(){
  REP(i,(n - 1) * m) nn[i] = nums[i];
  sort(nn, nn + (n - 1) * m);
  ns = unique(nn, nn + (n - 1) * m) - nn;
  memset(dp, 0, 1 << ns);
  return cango(0);
}

int restMin(){
  set<int> s(nums, nums + (n - 1) * m);
  vector<int> v(s.begin(), s.end());

  REP(i,n) REP(j,m) hit[i][j] = 0;
  REP(i,v.size()){
    bingoGo(v[i], true);
  }

  int ret = 1000;
  int *hh = hit[n - 1];

  // tate
  REP(i,m){
    int cnt = 0;
    REP(j,m) if(hh[j] & (1 << i))
      cnt++;
    ret = min(ret, m - cnt);
  }
  // yoko
  REP(i,m){
    int cnt = 0;
    REP(j,m) if(hh[i] & (1 << j))
      cnt++;
    ret = min(ret, m - cnt);
  }
  //naname1
  {
    int cnt = 0;
    REP(i,m) if(hh[i] & (1 << i))
      cnt++;
    ret = min(ret, m - cnt);
  }
  //naname2
  {
    int cnt = 0;
    REP(i,m) if(hh[i] & (1 << (m - i - 1)))
      cnt++;
    ret = min(ret, m - cnt);
  }
  return ret;
}

void solve(int pos){
  if(pos == n - 1){
    int tmp = set<int>(nums, nums + (n - 1) * m).size()
      + restMin();

    if(tmp < ans){
      REP(i,n) REP(j,m) hit[i][j] = 0;
      if(check()) ans = tmp;
    }
    return;
  }

  // tate
  REP(i,m){
    REP(j,m){
      nums[pos * m + j] = bingo[pos][j][i];
    }
    solve(pos + 1);
  }

  // yoko
  REP(i,m){
    REP(j,m){
      nums[pos * m + j] = bingo[pos][i][j];
    }
    solve(pos + 1);
  }

  // naname1
  REP(i,m){
    nums[pos * m + i] = bingo[pos][i][i];
  }
  solve(pos + 1);

  // naname2
  REP(i,m){
    nums[pos * m + i] = bingo[pos][m - i - 1][i];
  }
  solve(pos + 1);
}

int main(){
  while(true){
    n = getInt();
    m = getInt();

    if(n + m == 0) break;

    REP(i,100) memo[i] = vector<pair<int, pair<int, int> > >();

    REP(i,n){
      REP(j,m) REP(k,m){
	bingo[i][j][k] = getInt();
	memo[bingo[i][j][k]].push_back(make_pair(i, make_pair(j, k)));
      }
    }

    ans = 1000;
    solve(0);
    if(ans == 1000) ans = 0;
    printf("%d\n", ans);
  }
  return 0;
}

Measured memory_bytes: 940