#include <bits/stdc++.h>
#pragma comment(linker, "/STACK:200000000")
using namespace std;
template <typename T>
inline T Abs(T x) {
  return (x >= 0) ? x : -x;
}
template <typename T>
inline T sqr(T x) {
  return x * x;
}
template <typename T>
inline string toStr(T x) {
  stringstream st;
  st << x;
  string s;
  st >> s;
  return s;
}
template <typename T>
inline int bit(T mask, int b) {
  return (b >= 0 && (mask & (T(1) << b)) != 0) ? 1 : 0;
}
inline int nextInt() {
  int x;
  if (scanf("%d", &x) != 1) throw;
  return x;
}
inline long long nextInt64() {
  long long x;
  if (scanf("%I64d", &x) != 1) throw;
  return x;
}
inline double nextDouble() {
  double x;
  if (scanf("%lf", &x) != 1) throw;
  return x;
}
const int INF = (int)1E9;
const long long INF64 = (long long)1E18;
const long double EPS = 1E-9;
const long double PI = 3.1415926535897932384626433832795;
const int MAXN = 85;
const int ADD = MAXN * MAXN;
const int dx[] = {0, 0, -1, +1};
const int dy[] = {-1, +1, 0, 0};
const int MAXV = 4 * ADD + 100;
int n, m, a[MAXN][MAXN];
bool valid(int x, int y) { return (0 <= x && x < n) && (0 <= y && y < m); }
int getV(int type, int x, int y) { return type * ADD + x * m + y; }
struct edge {
  int v, u, c, cost, f;
  edge(int v, int u, int c, int cost) : v(v), u(u), c(c), cost(cost), f(0) {}
};
int d[MAXV], S = MAXV - 10, T = MAXV - 11, pr[MAXV];
vector<int> g[MAXV];
bool used[MAXV];
vector<edge> e;
queue<int> q;
void addEdge(int v, int u, int c, int cost) {
  g[v].push_back((int)e.size());
  e.push_back(edge(v, u, c, +cost));
  g[u].push_back((int)e.size());
  e.push_back(edge(u, v, 0, -cost));
}
void update(int v, int value, int prev) {
  if (d[v] > value) {
    if (!used[v]) q.push(v);
    d[v] = value;
    used[v] = true;
    pr[v] = prev;
  }
}
bool calcFlow(int &ansFlow, int &ansCost) {
  memset(d, 60, sizeof d);
  memset(used, 0, sizeof used);
  update(S, 0, -1);
  while (!q.empty()) {
    int v = q.front();
    q.pop();
    used[v] = false;
    for (int i = 0; i < (int)(g[v].size()); i++) {
      int id = g[v][i], u = e[id].u;
      if (e[id].c > e[id].f) update(u, d[v] + e[id].cost, id);
    }
  }
  if (d[T] > INF / 2) return false;
  int cflow = INF, ccost = 0;
  for (int v = T; v != S;) {
    int id = pr[v];
    cflow = min(cflow, e[id].c - e[id].f);
    v = e[id].v;
  }
  for (int v = T; v != S;) {
    int id = pr[v];
    e[id].f += cflow;
    e[id ^ 1].f -= cflow;
    ccost += e[id].cost * cflow;
    v = e[id].v;
  }
  ansFlow += cflow;
  ansCost += ccost;
  return true;
}
int main() {
  n = nextInt();
  m = nextInt();
  for (int i = 0; i < (int)(n); i++)
    for (int j = 0; j < (int)(m); j++) a[i][j] = nextInt() - 1;
  vector<pair<pair<int, int>, pair<int, int> > > p;
  for (int x = 0; x < (int)(n); x++)
    for (int y = 0; y < (int)(m); y++) {
      addEdge(S, x * m + y, 1, 0);
      addEdge(x * m + y + ADD, T, 1, 0);
      for (int i = 0; i < (int)(4); i++) {
        int xx = x + dx[i], yy = y + dy[i];
        if (valid(xx, yy) && (x + y) % 2 == 0) {
          int v = x * m + y, u = xx * m + yy;
          int cost = (a[x][y] == a[xx][yy]) ? -1 : 0;
          addEdge(v, u + ADD, 1, cost);
        }
      }
    }
  int flow = 0, cost = 0;
  while (calcFlow(flow, cost))
    ;
  cout << n * m / 2 + cost << endl;
  return 0;
}
