#include <bits/stdc++.h>
#pragma warning(disable : 4996)
using namespace std;
long long __gcd(long long a, long long b) {
  while (a != 0) {
    long long __t = b % a;
    b = a;
    a = __t;
  }
  return b;
}
class Graph {
 public:
  struct edge;

 private:
  unsigned N;
  vector<long long> pot;
  vector<vector<edge*> > a;
  bool Dijkstra(unsigned from, unsigned to, vector<edge*>& pr) {
    fill(pr.begin(), pr.end(), nullptr);
    vector<long long> d(N, (long long)1e18);
    d[from] = 0;
    priority_queue<pair<long long, unsigned>,
                   vector<pair<long long, unsigned> >,
                   greater<pair<long long, unsigned> > >
        q;
    q.push({0, from});
    while (!q.empty()) {
      long long cost = q.top().first;
      unsigned v = q.top().second;
      q.pop();
      if (cost > d[v]) continue;
      for (auto& e : a[v]) {
        if (e->capLeftFrom(v)) {
          unsigned u = e->from(v);
          if (d[u] > d[v] + e->costFrom(v) + pot[v] - pot[u]) {
            d[u] = d[v] + e->costFrom(v) + pot[v] - pot[u];
            pr[u] = e;
            q.push({d[u], u});
          }
        }
      }
    }
    if (d[to] == (long long)1e18) return false;
    for (unsigned i = 0; i < N; i++)
      pot[i] += (d[i] != (long long)1e18 ? d[i] : d[to]);
    return true;
  }

 public:
  struct edge {
    unsigned v1, v2;
    long long c1, c2;
    long long f1, f2;
    long long cost1, cost2;
    edge(unsigned v1, unsigned v2, long long c1, long long c2, long long cost1,
         long long cost2)
        : v1(v1),
          v2(v2),
          c1(c1),
          c2(c2),
          f1(0),
          f2(0),
          cost1(cost1),
          cost2(cost2) {}
    inline unsigned& from(unsigned v) { return (v == v1 ? v2 : v1); }
    inline long long& capFrom(unsigned v) { return (v == v1 ? c1 : c2); }
    inline long long& capTo(unsigned v) { return (v == v1 ? c2 : c1); }
    inline long long& flowFrom(unsigned v) { return (v == v1 ? f1 : f2); }
    inline long long& flowTo(unsigned v) { return (v == v1 ? f2 : f1); }
    inline long long capLeftFrom(unsigned v) const {
      return (v == v1 ? c1 - f1 : c2 - f2);
    }
    inline long long capLeftTo(unsigned v) const {
      return (v == v1 ? c2 - f2 : c1 - f1);
    }
    inline long long& costFrom(unsigned v) { return (v == v1 ? cost1 : cost2); }
    inline long long& costTo(unsigned v) { return (v == v1 ? cost2 : cost1); }
  };
  Graph() : N(0), a(N) {}
  Graph(unsigned N) : N(N), a(N), pot(N) {}
  inline void addEdge(unsigned v1, unsigned v2, long long cap1, long long cap2,
                      long long cost1, long long cost2) {
    edge* ne = new edge(v1, v2, cap1, cap2, cost1, cost2);
    a[v1].push_back(ne);
    a[v2].push_back(ne);
  }
  pair<int, int> maxFlowMinCost(unsigned from, unsigned to) {
    fill(pot.begin(), pot.end(), (long long)1e18);
    pot[from] = 0;
    for (unsigned i = 0; i < N; i++)
      for (unsigned j = 0; j < N; j++)
        if (pot[j] < (int)1e9)
          for (auto& e : a[j])
            if (j == e->v1 && pot[e->v2] > pot[j] + e->cost1)
              pot[e->v2] = pot[j] + e->cost1;
    long long resFlow = 0, resCost = 0;
    vector<edge*> pr(N, nullptr);
    while (Dijkstra(from, to, pr)) {
      long long newFlow = (int)1e9, newCost = 0;
      unsigned v = to;
      while (v != from) {
        newFlow = min(newFlow, pr[v]->capLeftTo(v));
        newCost += pr[v]->costTo(v);
        v = pr[v]->from(v);
      }
      v = to;
      while (v != from) {
        pr[v]->flowTo(v) += newFlow;
        pr[v]->flowFrom(v) -= newFlow;
        v = pr[v]->from(v);
      }
      resFlow += newFlow;
      resCost += newFlow * newCost;
    }
    return {resFlow, resCost};
  }
};
unsigned N, M;
int dx[] = {-1, 0, 1, 0};
int dy[] = {0, 1, 0, -1};
inline bool correctCord(int x, int y) {
  return (x >= 0 && x < N && y >= 0 && y < M);
}
inline unsigned getNum(unsigned x, unsigned y) { return 2 + x * M + y; }
int main() {
  cin.tie();
  cout.tie();
  cerr.tie();
  ios_base::sync_with_stdio(false);
  cin >> N >> M;
  vector<vector<int> > a(N, vector<int>(M));
  for (unsigned i = 0; i < N; i++)
    for (unsigned j = 0; j < M; j++) cin >> a[i][j];
  Graph g(2 + N * M);
  for (int i = 0; i < N; i++) {
    for (int j = 0; j < M; j++) {
      if ((i + j) % 2)
        g.addEdge(0, getNum(i, j), 1, 0, 0, 0);
      else
        g.addEdge(getNum(i, j), 1, 1, 0, 0, 0);
      if ((i + j) % 2) {
        for (int d = 0; d < 4; d++) {
          int nx = i + dx[d], ny = j + dy[d];
          if (correctCord(nx, ny))
            g.addEdge(getNum(i, j), getNum(nx, ny), 1, 0, a[i][j] != a[nx][ny],
                      -(a[i][j] != a[nx][ny]));
        }
      }
    }
  }
  cout << g.maxFlowMinCost(0, 1).second << endl;
  return 0;
}
