#include <bits/stdc++.h>
using namespace std;
template <typename T, typename U>
ostream& operator<<(ostream& os, const pair<T, U>& _p) {
  return os << "(" << _p.first << "," << _p.second << ")";
}
template <typename T>
ostream& operator<<(ostream& os, const vector<T>& _V) {
  bool f = true;
  os << "[";
  for (auto v : _V) {
    os << (f ? "" : ",") << v;
    f = false;
  }
  return os << "]";
}
template <typename T>
ostream& operator<<(ostream& os, const set<T>& _S) {
  bool f = true;
  os << "(";
  for (auto s : _S) {
    os << (f ? "" : ",") << s;
    f = false;
  }
  return os << ")";
}
template <typename T, typename U>
ostream& operator<<(ostream& os, const map<T, U>& _M) {
  return os << set<pair<T, U>>(_M.begin(), _M.end());
}
const signed long long INF = 1000000100;
const long double EPS = 1e-9;
namespace MCMF {
const int MAXN = 10005;
const int MAXM = 500500;
int nodes, nedge, src, dest;
int point[MAXM], nex[MAXM], flow[MAXM], capa[MAXM], cost[MAXM];
int head[MAXN], dist[MAXN], expand[MAXN], pre[MAXN], edge[MAXN];
bool changed[MAXN];
void init(int n, int s, int t) {
  nodes = n;
  src = s;
  dest = t;
  nedge = 0;
  for (int(i) = (0); (i) < (nodes); (i)++) head[i] = -1;
}
int add_edge(int u, int v, int c, int w) {
  point[nedge] = v, capa[nedge] = c, cost[nedge] = w, flow[nedge] = 0,
  nex[nedge] = head[u], head[u] = (nedge++);
  point[nedge] = u, capa[nedge] = 0, cost[nedge] = -w, flow[nedge] = 0,
  nex[nedge] = head[v], head[v] = (nedge++);
  return nedge - 2;
}
pair<int, int> run() {
  int mflow = 0, mcost = 0;
  while (true) {
    for (int(i) = (0); (i) < (nodes); (i)++)
      dist[i] = INF, pre[i] = -1, changed[i] = 0;
    dist[src] = 0;
    changed[src] = 1;
    expand[src] = INF;
    while (true) {
      bool ok = 1;
      for (int(i) = (0); (i) < (nodes); (i)++)
        if (changed[i]) {
          changed[i] = 0;
          for (int k = head[i]; k >= 0; k = nex[k]) {
            if (flow[k] < capa[k] && dist[i] + cost[k] < dist[point[k]]) {
              dist[point[k]] = dist[i] + cost[k];
              changed[point[k]] = 1;
              pre[point[k]] = i;
              edge[point[k]] = k;
              expand[point[k]] = min(expand[i], capa[k] - flow[k]);
              ok = 0;
            }
          }
        }
      if (ok) break;
    }
    if (pre[dest] < 0) break;
    int d = expand[dest];
    mflow += d;
    mcost += d * dist[dest];
    for (int k = dest; k != src; k = pre[k]) {
      flow[edge[k]] += d;
      flow[edge[k] ^ 1] -= d;
    }
  }
  return make_pair(mflow, mcost);
}
};  // namespace MCMF
int M, N;
const int MAXN = 88;
int A[MAXN][MAXN];
void read_data() {
  scanf("%d %d", &M, &N);
  for (int(i) = (1); (i) <= (M); (i)++)
    for (int(j) = (1); (j) <= (N); (j)++) scanf("%d", &A[i][j]);
}
int encode(int x, int y) { return x + M * (y - 1); }
bool ok(int x, int y) { return 1 <= x and x <= M and 1 <= y and y <= N; }
int dx[] = {-1, 1, 0, 0};
int dy[] = {0, 0, -1, 1};
void solve() {
  int S = M * N + 1;
  int T = M * N + 2;
  MCMF::init(T + 1, S, T);
  for (int(x) = (1); (x) <= (M); (x)++)
    for (int(y) = (1); (y) <= (N); (y)++) {
      if ((x + y) % 2 == 1) {
        MCMF::add_edge(S, encode(x, y), 1, 0);
        for (int(d) = (0); (d) <= (3); (d)++) {
          int nx = x + dx[d];
          int ny = y + dy[d];
          if (!ok(nx, ny)) continue;
          int w = (A[x][y] != A[nx][ny]);
          MCMF::add_edge(encode(x, y), encode(nx, ny), 1, w);
        }
      } else {
        MCMF::add_edge(encode(x, y), T, 1, 0);
      }
    }
  cout << MCMF::run().second << endl;
}
int main() {
  read_data();
  solve();
}
