#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();   }