#include <bits/stdc++.h>
template <typename T>
inline void tension(T &a, const T &b) {
  if (a > b) a = b;
}
template <typename T>
inline bool tensionv(T &a, const T &b) {
  return a > b ? a = b, true : false;
}
inline unsigned int getuint() {
  char w = getchar();
  while (w < '0' || '9' < w) w = getchar();
  unsigned int ans = 0;
  for (; '0' <= w && w <= '9'; w = getchar()) ans = ans * 10 + w - '0';
  return ans;
}
const int dx[] = {1, -1, 0, 0};
const int dy[] = {0, 0, 1, -1};
int a[233][233], id[233][233];
int V, S, T;
const int MAXV = 233333, MAXE = 233333;
struct Edge {
  int t, next, cap, v;
} G[MAXE];
int head[MAXV], e = 1;
inline void add_double_edge(int x, int y, int z, int v) {
  G[++e].t = y, G[e].next = head[x], head[x] = e;
  G[e].cap = z, G[e].v = v;
  G[++e].t = x, G[e].next = head[y], head[y] = e;
  G[e].cap = 0, G[e].v = -v;
}
struct ZKW_Min_Cost_Flow {
  int dist[MAXV], inq[MAXV];
  std::pair<int, int> heap[MAXE];
  int heap_size;
  void SPFA() {
    memset(dist + 1, 0x3f, sizeof(*dist) * V),
        heap[heap_size++] = std::make_pair(-(dist[T] = 0), T),
        std::push_heap(heap, heap + heap_size);
    for (int d, x; heap_size;) {
      d = -heap->first, x = heap->second,
      std::pop_heap(heap, heap + heap_size--);
      if (d != dist[x]) continue;
      for (int i = head[x]; i; i = G[i].next)
        if (G[i ^ 1].cap && tensionv(dist[G[i].t], dist[x] - G[i].v))
          heap[heap_size++] = std::make_pair(-dist[G[i].t], G[i].t),
          std::push_heap(heap, heap + heap_size);
    }
  }
  int dfs(int x, int aug) {
    if (x == T) return aug;
    inq[x] = 1;
    int now = aug;
    for (int i = head[x]; i; i = G[i].next)
      if (!inq[G[i].t] && G[i].cap && dist[x] == dist[G[i].t] + G[i].v) {
        int flow = dfs(G[i].t, std::min(aug, G[i].cap));
        if (flow) {
          G[i].cap -= flow, G[i ^ 1].cap += flow, now -= flow;
          if (!now) break;
        }
      }
    return aug - now;
  }
  bool modify_label() {
    int t = 0x3f3f3f3f;
    for (int x = 1; x <= V; x++)
      if (inq[x])
        for (int i = head[x]; i; i = G[i].next)
          if (!inq[G[i].t] && G[i].cap)
            tension(t, dist[G[i].t] + G[i].v - dist[x]);
    if (t == 0x3f3f3f3f) return false;
    for (int i = 1; i <= V; i++)
      if (inq[i]) dist[i] += t;
    return true;
  }
  int min_cost_flow() {
    SPFA();
    int ans = 0;
    for (;;) {
      for (int t;;) {
        memset(inq + 1, 0, sizeof(*inq) * V);
        if (t = dfs(S, 0x3f3f3f3f))
          ans += dist[S] * t;
        else
          break;
      }
      if (!modify_label()) break;
    }
    return ans;
  }
} MCF;
int main() {
  int n = getuint(), m = getuint();
  for (int i = 1; i <= n; i++)
    for (int j = 1; j <= m; id[i][j++] = ++V) a[i][j] = getuint();
  S = ++V, T = ++V;
  for (int i = 1; i <= n; i++)
    for (int j = 1; j <= m; j++)
      if (i + j & 1) {
        add_double_edge(S, id[i][j], 1, 0);
        for (int f = 0; f < 4; f++) {
          int x = i + dx[f], y = j + dy[f];
          if (x < 1 || x > n || y < 1 || y > m) continue;
          add_double_edge(id[i][j], id[x][y], 1, a[i][j] != a[x][y]);
        }
      } else
        add_double_edge(id[i][j], T, 1, 0);
  printf("%d\n", MCF.min_cost_flow());
  return 0;
}
