#include <bits/stdc++.h>
#pragma GCC optimize("Ofast")
#pragma GCC optimize("unroll-loops")
#pragma GCC target("sse,sse2,sse3,ssse3,sse4,popcnt,abm,mmx,avx,tune=native")
using namespace std;
const int INF = 1e9;
const int MAXN = 6405;
struct edge {
  int dest;
  long long origcap, cap;
  long long cost;
  int rev;
};
struct MCF {
  vector<edge> adj[MAXN];
  long long dis[MAXN], cost;
  int source, target, iter;
  long long cap;
  edge* pre[MAXN];
  int queued[MAXN];
  MCF() {}
  void add_edge(int from, int to, long long cap, long long cost) {
    adj[from].push_back(edge{to, cap, cap, cost, (int)adj[to].size()});
    adj[to].push_back(edge{from, 0, 0, -cost, (int)adj[from].size() - 1});
  }
  bool spfa() {
    for (int i = int(0); i < int(MAXN); ++i) queued[i] = 0;
    fill(dis, dis + MAXN, INF);
    queue<int> q;
    pre[source] = pre[target] = 0;
    dis[source] = 0;
    q.emplace(source);
    queued[source] = 1;
    while (!q.empty()) {
      int x = q.front();
      long long d = dis[x];
      q.pop();
      queued[x] = 0;
      for (auto& e : adj[x]) {
        int y = e.dest;
        long long w = d + e.cost;
        if (e.cap < 1 || dis[y] <= w) continue;
        dis[y] = w;
        pre[y] = &e;
        if (!queued[y]) {
          q.push(y);
          queued[y] = 1;
        }
      }
    }
    edge* e = pre[target];
    if (!e) return 0;
    while (e) {
      edge& rev = adj[e->dest][e->rev];
      e->cap -= cap;
      rev.cap += cap;
      cost += cap * e->cost;
      e = pre[rev.dest];
    }
    return 1;
  }
  pair<long long, long long> mincost(int S, int T) {
    cap = 1, source = S, target = T, cost = 0;
    while (spfa()) {
    }
    long long totflow = 0;
    for (auto e : adj[source]) {
      totflow += (e.origcap - e.cap);
    }
    return make_pair(totflow, cost);
  }
};
int grid[85][85];
int movX[4] = {0, 1, 0, -1};
int movY[4] = {1, 0, -1, 0};
int main() {
  int n, m;
  scanf("%d%d", &n, &m);
  for (int i = int(0); i < int(n); ++i)
    for (int j = int(0); j < int(m); ++j) {
      scanf("%d", &grid[i][j]);
    }
  int s = n * m, t = n * m + 1;
  MCF mcf;
  for (int i = int(0); i < int(n); ++i)
    for (int j = int(0); j < int(m); ++j) {
      if ((i + j) & 1)
        mcf.add_edge(i * m + j, t, 1, 0);
      else {
        mcf.add_edge(s, i * m + j, 1, 0);
        for (int k = int(0); k < int(4); ++k) {
          int nxtX = i + movX[k];
          int nxtY = j + movY[k];
          if (nxtX >= 0 && nxtX < n && nxtY >= 0 && nxtY < m) {
            mcf.add_edge(i * m + j, nxtX * m + nxtY, 1,
                         grid[i][j] != grid[nxtX][nxtY]);
          }
        }
      }
    }
  printf("%lld\n", mcf.mincost(s, t).second);
  return 0;
}
