import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.util.Arrays;
import java.util.StringTokenizer;

/**
 * @author pashka
 */
public class Main {

    private void solve() throws IOException {
        int n = nextInt();
        int m = nextInt();
        int[][] a = new int[n][m];
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < m; j++) {
                a[i][j] = nextInt();
            }
        }
        init(n * m + 2, n * m * 6);
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < m; j++) {
                int c = i * m + j;
                if ((i + j) % 2 == 0) {
                    addEdge(0, 2 + c, 1, 0);
                } else {
                    addEdge(2 + c, 1, 1, 0);
                }
                if (i < n - 1) {
                    int cost = 0;
                    if (a[i][j] != a[i + 1][j]) cost = 1;
                    addEdge(2 + c, 2 + c + m, 1, cost);
                    addEdge(2 + c + m, 2 + c, 1, cost);
                }
                if (j < m - 1) {
                    int cost = 0;
                    if (a[i][j] != a[i][j + 1]) cost = 1;
                    addEdge(2 + c, 2 + c + 1, 1, cost);
                    addEdge(2 + c + 1, 2 + c, 1, cost);
                }
            }
        }
        long res = minCostMaxFlow(0, 1);
        out.println(res);
    }

    private BufferedReader br;
    private PrintWriter out;
    private StringTokenizer st;

    String next() throws IOException {
        while (st == null || !st.hasMoreTokens()) {
            st = new StringTokenizer(br.readLine());
        }
        return st.nextToken();
    }

    int nextInt() throws IOException {
        return Integer.parseInt(next());
    }

    long nextLong() throws IOException {
        return Long.parseLong(next());
    }

    public static void main(String[] args) throws IOException {
        new Main().run();
    }

    private void run() throws IOException {
        br = new BufferedReader(new InputStreamReader(System.in));
//        br = new BufferedReader(new FileReader("input.txt"));
        out = new PrintWriter(System.out);
        solve();
        out.close();
    }

    void init(int n, int m) {
        m *= 2;
        this.n = n;
        this.m = m;
        last = 0;
        head = new int[n];
        nx = new int[m];
        dst = new int[m];
        src = new int[m];
        cp = new int[m];
        ct = new long[m];
        fl = new int[m];
        prev = new int[n];
        heap = new int[n];
        heapRev = new int[n];
        d = new long[n];
        bestDist = new long[n];
        for (int i = 0; i < n; i++) {
            heap[i] = i;
            heapRev[i] = i;
        }
        Arrays.fill(head, -1);
    }

    void addEdge(int x, int y, int capacity, int cost) {
        nx[last] = head[x];
        src[last] = x;
        dst[last] = y;
        cp[last] = capacity;
        ct[last] = cost;
        head[x] = last;
        last++;
        nx[last] = head[y];
        src[last] = y;
        dst[last] = x;
        cp[last] = 0;
        ct[last] = -cost;
        head[y] = last;
        last++;
    }

    // Returns the total cost. Max flow value will be in field maxFlow.
    long minCostMaxFlow(int s, int t) {
        for (int i = 0; i < n; i++) {
            d[i] = INF;
        }
        d[s] = 0;
        boolean ok = false;
        while (!ok) {
            ok = true;
            for (int j = 0; j < m; j++)
                if (fl[j] < cp[j]) {
                    int x = src[j];
                    int y = dst[j];
                    if (d[x] < INF) {
                        if (d[y] > d[x] + ct[j]) {
                            d[y] = d[x] + ct[j];
                            ok = false;
                        }
                    }
                }
        }
        long res = 0;
        maxFlow = 0;
        while (true) {
            dijkstra(s);
            if (bestDist[t] == INF) break;
            {
                int i = t;
                int f = Integer.MAX_VALUE;
                while (i != s) {
                    int e = prev[i];
                    f = Math.min(f, cp[e] - fl[e]);
                    i = src[e];
                }
                if (f < 0) throw new RuntimeException();
                maxFlow += f;
                i = t;
                while (i != s) {
                    int e = prev[i];
                    fl[e] += f;
                    fl[e ^ 1] -= f;
                    if (fl[e] > cp[e]) throw new RuntimeException();
                    res += ct[e] * f;
                    i = src[e];
                }
            }
            for (int i = 0; i < n; i++) {
                d[i] += bestDist[i];
            }
        }
        return res;
    }

    private static final long INF = Long.MAX_VALUE;

    int n, m;
    int[] head;
    int[] nx;
    int[] src;
    int[] dst;
    int[] cp;
    long[] ct;
    int[] fl;
    int last;

    long[] d;
    long[] bestDist;
    int[] prev;
    int[] heap;
    int[] heapRev;
    int heapSize;
    long maxFlow;

    private void dijkstra(int start) {
        Arrays.fill(bestDist, INF);
        heapSize = n;
        bestDist[start] = 0;
        siftUp(heapRev[start]);
        for (int i = 0; i < n; i++) {
            int x = removeMin();
            if (bestDist[x] == INF) {
                break;
            }
            int j = head[x];
            while (j >= 0) {
                if (fl[j] < cp[j]) {
                    int y = dst[j];
                    long d = ct[j] + this.d[x] - this.d[y];
                    if (d < 0) {
                        throw new AssertionError("negative edge");
                    }
                    long ll = bestDist[x] + d;
                    if (bestDist[y] > ll) {
                        prev[y] = j;
                        bestDist[y] = ll;
                        siftUp(heapRev[y]);
                    }
                }
                j = nx[j];
            }
        }
    }

    private void siftUp(int i) {
        while (i > 0 && bestDist[heap[(i - 1) / 2]] > bestDist[heap[i]]) {
            swap(i, (i - 1) / 2);
            i = (i - 1) / 2;
        }
    }

    private int removeMin() {
        int res = heap[0];
        swap(0, heapSize - 1);
        heapSize--;
        int i = 0;
        while (2 * i + 1 < heapSize) {
            int j = i;
            if (bestDist[heap[2 * i + 1]] < bestDist[heap[j]]) {
                j = 2 * i + 1;
            }
            if (2 * i + 2 < heapSize && bestDist[heap[2 * i + 2]] < bestDist[heap[j]]) {
                j = 2 * i + 2;
            }
            if (i == j) break;
            swap(i, j);
            i = j;
        }
        return res;
    }

    private void swap(int i, int j) {
        int t = heap[i];
        heap[i] = heap[j];
        heap[j] = t;
        heapRev[heap[i]] = i;
        heapRev[heap[j]] = j;
    }
}


import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.util.*;

public class C {
	BufferedReader reader;
    StringTokenizer tokenizer;
    PrintWriter out;

	public void solve() throws IOException {
		int N = nextInt();
		int M = nextInt();
		int[][] a = new int[N][M];
		for(int i = 0; i < N; i++)
			for(int j = 0; j < M; j++)
				a[i][j] = nextInt();

		int size = N*M + 2;
		int[][] cap = new int[size][size];
		int[][] cost = new int[size][size];
		int src = N*M;
		int dst = N*M+1;

		int[] dx = new int[]{-1,1,0,0};
		int[] dy = new int[]{0,0,-1,1};

		for(int i = 0; i < N; i++){
			for(int j = 0; j < M; j++){
				int index = i*M+j;
				if( (i+j) % 2 == 0){
					cap[src][index] = 1;
					for(int k = 0; k < 4; k++){
						int newx = i + dx[k];
						int newy = j + dy[k];
						if( newx < 0 || newx >= N || newy < 0 || newy >= M) continue;
						int newindex = newx*M + newy;
						cap[index][newindex] = 1;
						cost[index][newindex] = a[i][j] == a[newx][newy]? 0: 1;
//						cost[newindex][index] = -1* cost[index][newindex];
					}
				}
				else{
					cap[index][dst] = 1;
				}
			}
		}

		MinCostMaxFlow flow = new MinCostMaxFlow();
		int result[] = flow.getMaxFlow(cap, cost, src, dst);
		if( result[0] == N*M/2 )
			out.println(result[1]);
		else
			out.println(-1);
	}

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		new C().run();
	}

	public void run() {
        try {
            reader = new BufferedReader(new InputStreamReader(System.in));
            tokenizer = null;
            out = new PrintWriter(System.out);
            solve();
            reader.close();
            out.close();
        } catch (Exception e) {
            e.printStackTrace();
            System.exit(1);
        }
    }

    int nextInt() throws IOException {
        return Integer.parseInt(nextToken());
    }

    long nextLong() throws IOException {
        return Long.parseLong(nextToken());
    }

    double nextDouble() throws IOException {
        return Double.parseDouble(nextToken());
    }

    String nextToken() throws IOException {
        while (tokenizer == null || !tokenizer.hasMoreTokens()) {
            tokenizer = new StringTokenizer(reader.readLine());
        }
        return tokenizer.nextToken();
    }

}

//Min cost max flow algorithm using an adjacency matrix.  If you
//want just regular max flow, setting all edge costs to 1 gives
//running time O(|E|^2 |V|).
//
//Running time: O(min(|V|^2 * totflow, |V|^3 * totcost))
//
//INPUT: cap -- a matrix such that cap[i][j] is the capacity of
//          a directed edge from node i to node j
//
//   cost -- a matrix such that cost[i][j] is the (positive)
//           cost of sending one unit of flow along a
//           directed edge from node i to node j
//
//   source -- starting node
//   sink -- ending node
//
//OUTPUT: max flow and min cost; the matrix flow will contain
//    the actual flow values (note that unlike in the MaxFlow
//    code, you don't need to ignore negative flow values -- there
//    shouldn't be any)
//
//To use this, create a MinCostMaxFlow object, and call it like this:
//
//MinCostMaxFlow nf;
//int maxflow = nf.getMaxFlow(cap,cost,source,sink);

class MinCostMaxFlow {
	boolean found[];
	int N, cap[][], flow[][], cost[][], dad[], dist[], pi[];

	static final int INF = Integer.MAX_VALUE / 2 - 1;

	boolean search(int source, int sink) {
		Arrays.fill(found, false);
		Arrays.fill(dist, INF);
		dist[source] = 0;
		while (source != N) {
			int best = N;
			found[source] = true;
			for (int k = 0; k < N; k++) {
				if (found[k]) continue;
				if (flow[k][source] != 0) {
					int val = dist[source] + pi[source] - pi[k] - cost[k][source];
					if (dist[k] > val) {
						dist[k] = val;
						dad[k] = source;
					}
				}
				if (flow[source][k] < cap[source][k]) {
					int val = dist[source] + pi[source] - pi[k] + cost[source][k];
					if (dist[k] > val) {
						dist[k] = val;
						dad[k] = source;
					}
				}
				if (dist[k] < dist[best]) best = k;
			}
			source = best;
		}
		for (int k = 0; k < N; k++)
			pi[k] = Math.min(pi[k] + dist[k], INF);
		return found[sink];
	}

	int[] getMaxFlow(int cap[][], int cost[][], int source, int sink) {
		this.cap = cap;
		this.cost = cost;

		N = cap.length;
		found = new boolean[N];
		flow = new int[N][N];
		dist = new int[N+1];
		dad = new int[N];
		pi = new int[N];

		int totflow = 0, totcost = 0;
		while (search(source, sink)) {
			int amt = INF;
			for (int x = sink; x != source; x = dad[x])
				amt = Math.min(amt, flow[x][dad[x]] != 0 ? flow[x][dad[x]] :cap[dad[x]][x] - flow[dad[x]][x]);
			for (int x = sink; x != source; x = dad[x]) {
				if (flow[x][dad[x]] != 0) {
					flow[x][dad[x]] -= amt;
					totcost -= amt * cost[x][dad[x]];
				} else {
					flow[dad[x]][x] += amt;
					totcost += amt * cost[dad[x]][x];
				}
			}
			totflow += amt;
		}
		return new int[]{ totflow, totcost };
	}
}


import java.io.*;
import java.util.*;

public class Problem {

    class Rib {
        Vertex to;
        int capacity, weight, flow;
        Rib invert, next;

        Rib(Vertex to, int capacity, int weight) {
            this.to = to;
            this.capacity = capacity;
            this.weight = weight;
            this.flow = 0;
            next = null;
        }
    }

    class Vertex {
        Rib last;
        int phi, dist;
        int flow;
        boolean used;
        Rib way;

        Vertex() {
            last = null;
            phi = dist = 0;
            flow = 0;
            used = false;
            way = null;
        }
    }

    Scanner in = new Scanner(System.in);
    PrintWriter out = new PrintWriter(System.out);

    int minCostMaxFlow(Vertex[] g) {
        int n = g.length - 2;
        Vertex s, t;
        int cost = 0;

        while (true) {
            s = g[n];
            t = g[n + 1];
            dejkstra(g, s);
            if (t.way == null) break;
            int df = t.flow;
//            out.println("(" + t.dist + "-(" + s.phi + "-" + t.phi + "))*" + t.flow);
            cost += (t.dist - (s.phi - t.phi)) * t.flow;
            while (t.way != null) {
                t.way.flow += df;
                t.way.invert.flow -= df;
                t = t.way.invert.to;
//                outVertex(t, g, g[g.length - 2], g[g.length - 1]);
//                out.print(" ");
            }
//            out.println();
        }

        return cost;
    }

    void outVertex(Vertex v, Vertex[] g, Vertex s, Vertex t) {
        if (v == s) out.print("s"); else
        if (v == t) out.print("t"); else
            for (int i = 0; i < g.length; i++) {
                if (v == g[i]) out.print(i);
            }
        out.flush();
    }
    int goCnt = 0;

    void dejkstra(Vertex[] g, Vertex s) {
        for (Vertex v : g) {
            v.way = null;
            v.phi += v.dist;
            v.dist = g.length * g.length;
            v.used = false;
        }
        s.flow = g.length * g.length;
        class GoInfo {
            int dist;
            Rib rib;
            Vertex vertex;
            int ind;

            GoInfo(int dist, Vertex vertex, Rib rib, int ind) {
                this.dist = dist;
                this.vertex = vertex;
                this.rib = rib;
                this.ind = ind;
            }
        }
        class GoComp implements Comparator<GoInfo> {
            public int compare(GoInfo a, GoInfo b) {
                if (a.dist == b.dist) {
                    return a.ind - b.ind;
                }
                return a.dist - b.dist;
            }
        }
        TreeSet<GoInfo> q = new TreeSet<GoInfo>(new GoComp());
        q.add(new GoInfo(0, s, null, goCnt++));
        while (!q.isEmpty()) {
            GoInfo info = q.first();
            q.remove(info);
            Vertex v = info.vertex;
            if (v.used) continue;

//            out.print(info.dist + " [");
//            if (info.rib != null) outVertex(info.rib.invert.to, g, s, g[g.length - 1]);
//            out.print(" ");
//            outVertex(v, g, s, g[g.length - 1]);
//            out.println("]");
            v.dist = info.dist;
            v.way = info.rib;
            v.used = true;
            if (info.rib != null) v.flow = Math.min(info.rib.invert.to.flow, info.rib.capacity - info.rib.flow);
            for (Rib rib = v.last; rib != null; rib = rib.next) {
                if (rib.flow == rib.capacity) continue;
                q.add(new GoInfo(v.dist + rib.weight + (v.phi - rib.to.phi), rib.to, rib, goCnt++));
            }
        }
//        for (int i = 0; i < g.length; i++) {
//            out.print("d[");
//            outVertex(g[i], g, g[g.length - 2], g[g.length - 1]);
//            out.println("] = dist(" + g[i].dist + ") phi(" + g[i].phi + ")");
//        }
//        out.println("\n");
    }

    void addRib(Vertex from, Vertex to, int capacity, int weight) {
        Rib direct = new Rib(to, capacity, weight);
        Rib invert = new Rib(from, 0, -weight);
        direct.invert = invert;
        invert.invert = direct;
        direct.next = from.last;
        from.last = direct;
        invert.next = to.last;
        to.last = invert;
    }

    boolean inRange(int x, int min, int max) {
        return x >= min && x <= max;
    }

    Vertex[] readAndMakeGraph() {
        int n = in.nextInt();
        int m = in.nextInt();
        int[][] a = new int[n][m];
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < m; j++) {
                a[i][j] = in.nextInt();
            }
        }
        Vertex[] v = new Vertex[n * m + 2];
        for (int i = 0; i < n * m + 2; i++) v[i] = new Vertex();
        Vertex s = v[n * m];
        Vertex t = v[n * m + 1];
        for (int i = 0; i < n; i++) {
            for (int j = i % 2; j < m; j += 2) {
                for (int dx = -1; dx <= 1; dx++) {
                    for (int dy = -1; dy <= 1; dy++) {
                        if (Math.abs(dx) + Math.abs(dy) == 1) {
                            int ni = i + dx;
                            int nj = j + dy;
                            if (inRange(ni, 0, n - 1) && inRange(nj, 0, m - 1)) {
                                addRib(v[i * m + j], v[ni * m + nj], 1, (a[i][j] == a[ni][nj]? 0 : 1));
                            }
                        }
                    }
                }
            }
        }
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < m; j++) {
                if ((i + j) % 2 == 0) {
                    addRib(s, v[i * m + j], 1, 0);
                } else {
                    addRib(v[i * m + j], t, 1, 0);
                }
            }
        }
        v[v.length - 1].last = null;

        return v;
    }

    void solve() {
        out.print(minCostMaxFlow(readAndMakeGraph()));
        out.close();
    }

    public static void main(String[] args) {
        (new Problem()).solve();
    }
}


import java.io.InputStreamReader;
import java.io.IOException;
import java.util.Arrays;
import java.io.BufferedReader;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.util.StringTokenizer;
import java.io.InputStream;

/**
 * Built using CHelper plug-in
 * Actual solution is at the top
 */
public class Main {
	public static void main(String[] args) {
		InputStream inputStream = System.in;
		OutputStream outputStream = System.out;
		InputReader in = new InputReader(inputStream);
		PrintWriter out = new PrintWriter(outputStream);
		TaskC1 solver = new TaskC1();
		solver.solve(1, in, out);
		out.close();
	}
}

class TaskC1 {
    public void solve(int testNumber, InputReader in, PrintWriter out) {
        int n = in.nextInt();
        int m = in.nextInt();
        int[][] a = new int[n][m];
        for (int i = 0; i < n; ++i)
            for (int j = 0; j < m; ++j)
                a[i][j] = in.nextInt();
        int nv = n * m + 2;
        int s = nv - 2;
        int t = nv - 1;
        int[][] cap = new int[nv][nv];
        int[][] cost = new int[nv][nv];
        for (int i = 0; i < n; ++i)
            for (int j = 0; j < m; ++j) {
                if ((i + j) % 2 == 0) {
                    cap[s][i * m + j] = 1;
                    for (int di = -1; di <= 1; ++di)
                        for (int dj = -1; dj <= 1; ++dj)
                            if (Math.abs(di) + Math.abs(dj) == 1) {
                                int ni = i + di;
                                int nj = j + dj;
                                if (ni >= 0 && ni < n && nj >= 0 && nj < m) {
                                    cap[i * m + j][ni * m + nj] = 1;
                                    cost[i * m + j][ni * m + nj] = (a[i][j] == a[ni][nj] ? 0 : 1);
                                    cost[ni * m + nj][i * m + j] = -cost[i * m + j][ni * m + nj];
                                }
                            }
                } else {
                    cap[i * m + j][t] = 1;
                }
            }
        FlowRes fr = minCostMaxFlow(nv, s, t, cap, cost);
        if (fr.flow != n * m / 2) throw new RuntimeException();
        out.println(fr.cost);
    }
    static class FlowRes {
        int flow;
        int cost;
    }

    static final int INF = (int) 1e9;

    private FlowRes minCostMaxFlow(int n, int s, int t, int[][] cap, int[][] cost) {
        int[] pri = new int[n];
        FlowRes res = new FlowRes();
        while (true) {
            int[] dist = new int[n];
            int[] prev = new int[n];
            Arrays.fill(prev, -1);
            Arrays.fill(dist, INF);
            dist[s] = 0;
            boolean[] mark = new boolean[n];
            int mxd = 0;
            while (true) {
                int bi = -1;
                for (int i = 0; i < n; ++i) if (dist[i] < INF / 2 && !mark[i]) {
                    if (bi < 0 || dist[i] < dist[bi])
                        bi = i;
                }
                if (bi < 0) break;
                mark[bi] = true;
                mxd = dist[bi];
                for (int i = 0; i < n; ++i) if (!mark[i] && cap[bi][i] > 0) {
                    int edge = pri[bi] - pri[i] + cost[bi][i];
                    if (dist[i] > dist[bi] + edge) {
                        dist[i] = dist[bi] + edge;
                        prev[i] = bi;
                    }
                }
            }
            if (!mark[t]) break;
            ++res.flow;
            res.cost += dist[t] - pri[s] + pri[t];
            int at = t;
            while (at != s) {
                int pat = prev[at];
                --cap[pat][at];
                ++cap[at][pat];
                at = pat;
            }
            for (int i = 0; i < n; ++i) {
                if (dist[i] > INF / 2) pri[i] += mxd; else pri[i] += dist[i];
            }
        }
        return res;
    }
}

class InputReader {
    public BufferedReader reader;
    public StringTokenizer tokenizer;

    public InputReader(InputStream stream) {
        reader = new BufferedReader(new InputStreamReader(stream));
        tokenizer = null;
    }

    public String next() {
        while (tokenizer == null || !tokenizer.hasMoreTokens()) {
            try {
                tokenizer = new StringTokenizer(reader.readLine());
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
        return tokenizer.nextToken();
    }

    public int nextInt() {
        return Integer.parseInt(next());
    }

    }


import java.io.*;
import java.util.*;

public class C_new {

	BufferedReader br;
	PrintWriter out;
	StringTokenizer st;
	boolean eof;

	int n, m;

	class Edge {
		int to, flow, cap, cost;
		Edge rev;

		public Edge(int to, int cap, int cost) {
			this.to = to;
			this.cap = cap;
			this.cost = cost;
		}
	}

	static final int[] dx = { -1, 0, 1, 0 };
	static final int[] dy = { 0, -1, 0, 1 };
	static final int INF = Integer.MAX_VALUE / 3;

	List<Edge>[] g;

	void addEdge(int v1, int v2, int cost) {
		Edge e1 = new Edge(v2, 1, cost);
		Edge e2 = new Edge(v1, 0, -cost);
		e1.rev = e2;
		e2.rev = e1;
		g[v1].add(e1);
		g[v2].add(e2);
	}

	int S, T;

	void solve() throws IOException {
		n = nextInt();
		m = nextInt();

		int[][] f = new int[n][m];
		for (int i = 0; i < n; i++)
			for (int j = 0; j < m; j++)
				f[i][j] = nextInt();

		g = new ArrayList[n * m + 2];
		S = n * m;
		T = n * m + 1;
		for (int i = 0; i < n * m; i++)
			g[i] = new ArrayList<>(4);
		g[S] = new ArrayList<>(n * m / 2);
		g[T] = new ArrayList<>(n * m / 2);

		for (int i = 0; i < n; i++)
			for (int j = 0; j < m; j++) {
				int v = i * m + j;
				int col = (i + j) & 1;
				if (col == 0) {
					addEdge(S, v, 0);
					for (int dir = 0; dir < 4; dir++) {
						int ni = i + dx[dir];
						int nj = j + dy[dir];
						if (ni >= 0 && ni < n && nj >= 0 && nj < m) {
							int to = ni * m + nj;
							int cost = (f[i][j] == f[ni][nj] ? 0 : 1);
							addEdge(v, to, cost);
						}
					}
				} else {
					addEdge(v, T, 0);
				}
			}

		int ans = 0;

		final int[] d = new int[n * m + 2];
		Edge[] from = new Edge[n * m + 2];

		Integer[] Integers = new Integer[n * m + 2];
		for (int i = 0; i < n * m + 2; i++)
			Integers[i] = i;

		TreeSet<Integer> set = new TreeSet<>(new Comparator<Integer>() {

			@Override
			public int compare(Integer o1, Integer o2) {
				if (d[o1] != d[o2])
					return Integer.compare(d[o1], d[o2]);
				return o1.compareTo(o2);
			}

		});

		for (int iter = 0; iter < n * m / 2; iter++) {

			Arrays.fill(d, INF);
			d[S] = 0;

			for (int i = 0; i < n * m + 2; i++)
				set.add(Integers[i]);

			while (true) {
				int v = set.pollFirst();

//				System.err.println(v + " " + Arrays.toString(d));

				if (v == T) {
					break;
				}

				for (int i = 0; i < g[v].size(); i++) {
					Edge e = g[v].get(i);
					if (e.flow < e.cap) {
						if (d[e.to] > d[v] + e.cost) {
							set.remove(Integers[e.to]);
							from[e.to] = e;
							d[e.to] = d[v] + e.cost;
							set.add(Integers[e.to]);
						}
					}
				}
			}

			for (int v = T; v != S; ) {
				Edge last = from[v];
				last.flow++;
				last.rev.flow--;
				ans += last.cost;
				v = last.rev.to;
			}

		}

		out.println(ans);

	}

	C_new() throws IOException {
		br = new BufferedReader(new InputStreamReader(System.in));
		out = new PrintWriter(System.out);
		solve();
		out.close();
	}

	public static void main(String[] args) throws IOException {
		new C_new();
	}

	String nextToken() {
		while (st == null || !st.hasMoreTokens()) {
			try {
				st = new StringTokenizer(br.readLine());
			} catch (Exception e) {
				eof = true;
				return null;
			}
		}
		return st.nextToken();
	}

	String nextString() {
		try {
			return br.readLine();
		} catch (IOException e) {
			eof = true;
			return null;
		}
	}

	int nextInt() throws IOException {
		return Integer.parseInt(nextToken());
	}

	long nextLong() throws IOException {
		return Long.parseLong(nextToken());
	}

	double nextDouble() throws IOException {
		return Double.parseDouble(nextToken());
	}
}


import java.io.*;
import java.util.*;

public class C1 {

    int n, m, k;
    int[][] f;
    boolean[][] c;
    boolean[] mask;
    int best = Integer.MAX_VALUE;

    void calc(int i, int j, int cnt) {
        if (cnt < best) {
            if (i == n - 1 && j == m) {
                best = cnt;
            } else {
                if (j == m) {
                    j = 0;
                    ++i;
                }
                if (c[i][j]) {
                    calc(i, j + 1, cnt);
                } else {
                    c[i][j] = true;
                    int a = f[i][j];
                    if (j < m - 1 && !c[i][j + 1]) {
                        int b = f[i][j + 1];
                        c[i][j + 1] = true;
                        mask[a] = true;
                        calc(i, j + 2, a == b ? cnt : cnt + 1);
                        mask[a] = false;
                        /*
                        if (a != b) {
                            mask[b] = true;
                            calc(i, j + 2, cnt + 1);
                            mask[b] = false;
                        }
                        */
                        c[i][j + 1] = false;
                    }
                    if (i < n - 1 && !c[i + 1][j]) {
                        int b = f[i + 1][j];
                        c[i + 1][j] = true;
                        mask[a] = true;
                        calc(i, j + 1, a == b ? cnt : cnt + 1);
                        mask[a] = false;
                        /*
                        if (a != b) {
                            mask[b] = true;
                            calc(i, j + 1, cnt + 1);
                            mask[b] = false;
                        }
                        */
                        c[i + 1][j] = false;
                    }
                    c[i][j] = false;
                }
            }
        }
    }

    void solve() throws IOException {
        in("__std"); out("__std");

        n = readInt();
        m = readInt();
        k = n * m / 2;
        f = new int[n][m];
        c = new boolean[n][m];
        mask = new boolean[k];
        for (int i = 0; i < n; ++i) {
            for (int j  = 0; j < m; ++j) {
                f[i][j] = readInt() - 1;
            }
        }
        calc(0, 0, 0);
        println(best);

        exit();
    }

    void in(String name) throws IOException {
        if (name.equals("__std")) {
            in = new BufferedReader(new InputStreamReader(System.in));
        } else {
            in = new BufferedReader(new FileReader(name));
        }
    }

    void out(String name) throws IOException {
        if (name.equals("__std")) {
            out = new PrintWriter(System.out);
        } else {
            out = new PrintWriter(name);
        }
    }

    void exit() {
        out.close();
        System.exit(0);
    }

    char readChar() throws IOException {
        return (char) in.read();
    }

    int readInt() throws IOException {
        return Integer.parseInt(readToken());
    }

    long readLong() throws IOException {
        return Long.parseLong(readToken());
    }

    double readDouble() throws IOException {
        return Double.parseDouble(readToken());
    }

    String readLine() throws IOException {
        st = null;
        return in.readLine();
    }

    String readToken() throws IOException {
        while (st == null || !st.hasMoreTokens()) {
            st = new StringTokenizer(in.readLine());
        }
        return st.nextToken();
    }

    boolean eof() throws IOException {
        return !in.ready();
    }

    void print(String format, Object ... args) {
        out.print(new Formatter(Locale.US).format(format, args));
    }

    void println(String format, Object ... args) {
        out.println(new Formatter(Locale.US).format(format, args));
    }

    void print(Object value) {
        out.print(value);
    }

    void println(Object value) {
        out.println(value);
    }

    void println() {
        out.println();
    }

    StringTokenizer st;

    BufferedReader in;
    PrintWriter out;

    public static void main(String[] args) throws IOException {
        new C1().solve();
    }
}


import java.io.*;
import java.util.*;

public class C {

	BufferedReader br;
	PrintWriter out;
	StringTokenizer st;
	boolean eof;

	int n, m;

	class Edge {
		int to, flow, cap, cost;
		Edge rev;

		public Edge(int to, int cap, int cost) {
			this.to = to;
			this.cap = cap;
			this.cost = cost;
		}
	}

	static final int[] dx = { -1, 0, 1, 0 };
	static final int[] dy = { 0, -1, 0, 1 };
	static final int INF = Integer.MAX_VALUE / 3;

	List<Edge>[] g;

	void addEdge(int v1, int v2, int cost) {
		Edge e1 = new Edge(v2, 1, cost);
		Edge e2 = new Edge(v1, 0, -cost);
		e1.rev = e2;
		e2.rev = e1;
		g[v1].add(e1);
		g[v2].add(e2);
	}

	int S, T;

	void solve() throws IOException {
		n = nextInt();
		m = nextInt();

		int[][] f = new int[n][m];
		for (int i = 0; i < n; i++)
			for (int j = 0; j < m; j++)
				f[i][j] = nextInt();

		g = new ArrayList[n * m + 2];
		S = n * m;
		T = n * m + 1;
		for (int i = 0; i < n * m; i++)
			g[i] = new ArrayList<>(5);
		g[S] = new ArrayList<>(n * m / 2);
		g[T] = new ArrayList<>(n * m / 2);

		for (int i = 0; i < n; i++)
			for (int j = 0; j < m; j++) {
				int v = i * m + j;
				int col = (i + j) & 1;
				if (col == 0) {
					addEdge(S, v, 0);
					for (int dir = 0; dir < 4; dir++) {
						int ni = i + dx[dir];
						int nj = j + dy[dir];
						if (ni >= 0 && ni < n && nj >= 0 && nj < m) {
							int to = ni * m + nj;
							int cost = (f[i][j] == f[ni][nj] ? 0 : 1);
							addEdge(v, to, cost);
						}
					}
				} else {
					addEdge(v, T, 0);
				}
			}

		int ans = 0;
		int[] pi = new int[n * m + 2];

		final int[] d = new int[n * m + 2];
		Edge[] from = new Edge[n * m + 2];

		Integer[] Integers = new Integer[n * m + 2];
		for (int i = 0; i < n * m + 2; i++)
			Integers[i] = i;

		TreeSet<Integer> set = new TreeSet<>(new Comparator<Integer>() {

			@Override
			public int compare(Integer o1, Integer o2) {
				if (d[o1] != d[o2])
					return Integer.compare(d[o1], d[o2]);
				return o1.compareTo(o2);
			}

		});

		int[] permLabel = new int[n * m + 2];
		int permLabelSz = 0;

		for (int iter = 0; iter < n * m / 2; iter++) {

			Arrays.fill(d, INF);
			d[S] = 0;

			set.add(Integers[S]);

			permLabelSz = 0;

			while (true) {
				int v = set.pollFirst();

				if (v == T) {
					set.clear();
					break;
				}

				permLabel[permLabelSz++] = v;

				for (int i = 0; i < g[v].size(); i++) {
					Edge e = g[v].get(i);
					if (e.flow < e.cap) {
						int redCost = e.cost - pi[v] + pi[e.to];
						if (redCost < 0)
							throw new AssertionError();
						if (d[e.to] > d[v] + redCost) {
							set.remove(Integers[e.to]);
							from[e.to] = e;
							d[e.to] = d[v] + redCost;
							set.add(Integers[e.to]);
						}
					}
				}
			}

			for (int i = 0; i < permLabelSz; i++) {
				int v = permLabel[i];
				pi[v] -= d[v] - d[T];
			}

			for (int v = T; v != S; ) {
				Edge last = from[v];
				last.flow++;
				last.rev.flow--;
				ans += last.cost;
				v = last.rev.to;
			}

		}

		out.println(ans);

	}

	C() throws IOException {
		br = new BufferedReader(new InputStreamReader(System.in));
		out = new PrintWriter(System.out);
		solve();
		out.close();
	}

	public static void main(String[] args) throws IOException {
		new C();
	}

	String nextToken() {
		while (st == null || !st.hasMoreTokens()) {
			try {
				st = new StringTokenizer(br.readLine());
			} catch (Exception e) {
				eof = true;
				return null;
			}
		}
		return st.nextToken();
	}

	String nextString() {
		try {
			return br.readLine();
		} catch (IOException e) {
			eof = true;
			return null;
		}
	}

	int nextInt() throws IOException {
		return Integer.parseInt(nextToken());
	}

	long nextLong() throws IOException {
		return Long.parseLong(nextToken());
	}

	double nextDouble() throws IOException {
		return Double.parseDouble(nextToken());
	}
}


import java.util.*;
import java.io.*;
import java.math.*;
import java.awt.geom.*;

import static java.lang.Math.*;

public class Solution implements Runnable {

	class Edge {

		int to, capacity, cost;
		int flow = 0;
		Edge compl = null;

		public Edge(int to, int capacity, int cost) {
			this.to = to;
			this.capacity = capacity;
			this.cost = cost;
		}

	}

	int n, m;
	Edge[][] graph;
	int[] size;

	void addEdge(int from, int to, int cost) {
		Edge e1 = new Edge(to, 1, cost);
		Edge e2 = new Edge(from, 0, -cost);
		e1.compl = e2;
		e2.compl = e1;
		graph[from][size[from]++] = e1;
		graph[to][size[to]++] = e2;
	}

	boolean inBounds(int i, int j) {
		return i >= 0 && i < n && j >= 0 && j < m;
	}

	int getIndex(int i, int j) {
		return i * m + j;
	}

	void randomShuffle(int[] arr) {
		Random rnd = new Random();
		for (int i = arr.length - 1; i >= 0; i--) {
			int pos = rnd.nextInt(i + 1);
			int temp = arr[pos];
			arr[pos] = arr[i];
			arr[i] = temp;
		}
	}

	public void solve() throws Exception {
		n = sc.nextInt();
		m = sc.nextInt();
		int[][] field = new int[n][m];

		int[] arr = new int[n * m];
		for (int i = 0; i < n * m; i += 2) {
			arr[i] = arr[i + 1] = i / 2 + 1;
		}

		randomShuffle(arr);

		for (int i = 0; i < n; i++) {
			for (int j = 0; j < m; j++) {
				field[i][j] = sc.nextInt();
			}
		}
		graph = new Edge[n * m + 2][20];
		size = new int[n * m + 2];
		int source = n * m;
		int sink = n * m + 1;
		graph[source] = new Edge[n * m];
		graph[sink] = new Edge[n * m];
		int gsize = n * m + 2;
		for (int i = 0; i < n; i++) {
			for (int j = 0; j < m; j++) {
				if ((i + j) % 2 == 0) {
					addEdge(source, getIndex(i, j), 0);
					if (inBounds(i - 1, j)) {
						if (field[i - 1][j] == field[i][j]) {
							addEdge(getIndex(i, j), getIndex(i - 1, j), 0);
						} else {
							addEdge(getIndex(i, j), getIndex(i - 1, j), 1);
						}
					}
					if (inBounds(i + 1, j)) {
						if (field[i + 1][j] == field[i][j]) {
							addEdge(getIndex(i, j), getIndex(i + 1, j), 0);
						} else {
							addEdge(getIndex(i, j), getIndex(i + 1, j), 1);
						}
					}
					if (inBounds(i, j - 1)) {
						if (field[i][j - 1] == field[i][j]) {
							addEdge(getIndex(i, j), getIndex(i, j - 1), 0);
						} else {
							addEdge(getIndex(i, j), getIndex(i, j - 1), 1);
						}
					}
					if (inBounds(i, j + 1)) {
						if (field[i][j + 1] == field[i][j]) {
							addEdge(getIndex(i, j), getIndex(i, j + 1), 0);
						} else {
							addEdge(getIndex(i, j), getIndex(i, j + 1), 1);
						}
					}
				} else {
					addEdge(getIndex(i, j), sink, 0);
				}
			}
		}

		int[] dist = new int[gsize];
		int[] from = new int[gsize];
		Edge[] byEdge = new Edge[gsize];
		Deque<Integer> q = new ArrayDeque<Integer>();

		int cost = 0;
		int flow = 0;

		while (true) {
			Arrays.fill(dist, Integer.MAX_VALUE / 4);
			q.clear();
			Arrays.fill(from, -1);
			Arrays.fill(byEdge, null);
			dist[source] = 0;
			q.offer(source);
			while (!q.isEmpty()) {
				int v = q.poll();
				for (int i = 0; i < size[v]; i++) {
					Edge e = graph[v][i];
					if (e.capacity - e.flow > 0 && dist[e.to] > dist[v] + e.cost) {
						dist[e.to] = dist[v] + e.cost;
						from[e.to] = v;
						byEdge[e.to] = e;
						if (e.cost == 0) {
							q.offerFirst(e.to);
						} else {
							q.offerLast(e.to);
						}
					}
				}
			}
			if (dist[sink] == Integer.MAX_VALUE / 4) {
				break;
			}
			cost += dist[sink];
			flow++;
			int curv = sink;
			while (from[curv] != -1) {
				Edge e = byEdge[curv];
				Edge compl = e.compl;
				e.flow++;
				compl.flow--;
				curv = from[curv];
			}
		}

		out.println(cost);
	}

	static Throwable uncaught;

	BufferedReader in;
	FastScanner sc;
	PrintWriter out;

	@Override
	public void run() {
		try {
			in = new BufferedReader(new InputStreamReader(System.in));
			out = new PrintWriter(System.out);
			sc = new FastScanner(in);
			solve();
		} catch (Throwable uncaught) {
			Solution.uncaught = uncaught;
		} finally {
			out.close();
		}
	}

	public static void main(String[] args) throws Throwable {
		Thread thread = new Thread(null, new Solution(), "", (1 << 26));
		thread.start();
		thread.join();
		if (Solution.uncaught != null) {
			throw Solution.uncaught;
		}
	}

}

class FastScanner {

	BufferedReader in;
	StringTokenizer st;

	public FastScanner(BufferedReader in) {
		this.in = in;
	}

	public String nextToken() throws Exception {
		while (st == null || !st.hasMoreTokens()) {
			st = new StringTokenizer(in.readLine());
		}
		return st.nextToken();
	}

	public int nextInt() throws Exception {
		return Integer.parseInt(nextToken());
	}

	public long nextLong() throws Exception {
		return Long.parseLong(nextToken());
	}

	public double nextDouble() throws Exception {
		return Double.parseDouble(nextToken());
	}

}


import java.io.*;
import java.util.*;

public class C {

	BufferedReader br;
	PrintWriter out;
	StringTokenizer st;
	boolean eof;

	int n, m;

	class Edge {
		int to, flow, cap, cost;
		Edge rev;

		public Edge(int to, int cap, int cost) {
			this.to = to;
			this.cap = cap;
			this.cost = cost;
		}
	}

	static final int[] dx = { -1, 0, 1, 0 };
	static final int[] dy = { 0, -1, 0, 1 };
	static final int INF = Integer.MAX_VALUE / 3;

	List<Edge>[] g;

	void addEdge(int v1, int v2, int cost) {
		Edge e1 = new Edge(v2, 1, cost);
		Edge e2 = new Edge(v1, 0, -cost);
		e1.rev = e2;
		e2.rev = e1;
		g[v1].add(e1);
		g[v2].add(e2);
	}

	int S, T;

	void solve() throws IOException {
		n = nextInt();
		m = nextInt();

		int[][] f = new int[n][m];
		for (int i = 0; i < n; i++)
			for (int j = 0; j < m; j++)
				f[i][j] = nextInt();

		g = new ArrayList[n * m + 2];
		S = n * m;
		T = n * m + 1;
		for (int i = 0; i < n * m; i++)
			g[i] = new ArrayList<>(5);
		g[S] = new ArrayList<>(n * m / 2);
		g[T] = new ArrayList<>(n * m / 2);

		for (int i = 0; i < n; i++)
			for (int j = 0; j < m; j++) {
				int v = i * m + j;
				int col = (i + j) & 1;
				if (col == 0) {
					addEdge(S, v, 0);
					for (int dir = 0; dir < 4; dir++) {
						int ni = i + dx[dir];
						int nj = j + dy[dir];
						if (ni >= 0 && ni < n && nj >= 0 && nj < m) {
							int to = ni * m + nj;
							int cost = (f[i][j] == f[ni][nj] ? 0 : 1);
							addEdge(v, to, cost);
						}
					}
				} else {
					addEdge(v, T, 0);
				}
			}

		int ans = 0;
		int[] pi = new int[n * m + 2];

		final int[] d = new int[n * m + 2];
		Edge[] from = new Edge[n * m + 2];

		Integer[] Integers = new Integer[n * m + 2];
		for (int i = 0; i < n * m + 2; i++)
			Integers[i] = i;

		TreeSet<Integer> set = new TreeSet<>(new Comparator<Integer>() {

			@Override
			public int compare(Integer o1, Integer o2) {
				if (d[o1] != d[o2])
					return Integer.compare(d[o1], d[o2]);
				return o1.compareTo(o2);
			}

		});

		boolean[] permLabel = new boolean[n * m + 2];

		for (int iter = 0; iter < n * m / 2; iter++) {

			Arrays.fill(d, INF);
			d[S] = 0;

			set.add(Integers[S]);

			Arrays.fill(permLabel, false);

			while (true) {
				int v = set.pollFirst();

				if (v == T) {
					set.clear();
					break;
				}

				permLabel[v] = true;

				for (int i = 0; i < g[v].size(); i++) {
					Edge e = g[v].get(i);
					if (e.flow < e.cap) {
						int redCost = e.cost - pi[v] + pi[e.to];
						if (redCost < 0)
							throw new AssertionError();
						if (d[e.to] > d[v] + redCost) {
							set.remove(Integers[e.to]);
							from[e.to] = e;
							d[e.to] = d[v] + redCost;
							set.add(Integers[e.to]);
						}
					}
				}
			}

			for (int i = 0; i < n * m + 2; i++) {
				pi[i] -= (permLabel[i] ? d[i] : d[T]);
			}

			for (int v = T; v != S; ) {
				Edge last = from[v];
				last.flow++;
				last.rev.flow--;
				ans += last.cost;
				v = last.rev.to;
			}

		}

		out.println(ans);

	}

	C() throws IOException {
		br = new BufferedReader(new InputStreamReader(System.in));
		out = new PrintWriter(System.out);
		solve();
		out.close();
	}

	public static void main(String[] args) throws IOException {
		new C();
	}

	String nextToken() {
		while (st == null || !st.hasMoreTokens()) {
			try {
				st = new StringTokenizer(br.readLine());
			} catch (Exception e) {
				eof = true;
				return null;
			}
		}
		return st.nextToken();
	}

	String nextString() {
		try {
			return br.readLine();
		} catch (IOException e) {
			eof = true;
			return null;
		}
	}

	int nextInt() throws IOException {
		return Integer.parseInt(nextToken());
	}

	long nextLong() throws IOException {
		return Long.parseLong(nextToken());
	}

	double nextDouble() throws IOException {
		return Double.parseDouble(nextToken());
	}
}


import java.io.*;
import java.util.*;

public class C {

	BufferedReader br;
	PrintWriter out;
	StringTokenizer st;
	boolean eof;

	int n, m;

	class Edge {
		int to, flow, cap, cost;
		Edge rev;

		public Edge(int to, int cap, int cost) {
			this.to = to;
			this.cap = cap;
			this.cost = cost;
		}
	}

	static final int[] dx = { -1, 0, 1, 0 };
	static final int[] dy = { 0, -1, 0, 1 };
	static final int INF = Integer.MAX_VALUE / 3;

	List<Edge>[] g;

	void addEdge(int v1, int v2, int cost) {
		Edge e1 = new Edge(v2, 1, cost);
		Edge e2 = new Edge(v1, 0, -cost);
		e1.rev = e2;
		e2.rev = e1;
		g[v1].add(e1);
		g[v2].add(e2);
	}

	int S, T;

	void solve() throws IOException {
		n = nextInt();
		m = nextInt();

		int[][] f = new int[n][m];
		for (int i = 0; i < n; i++)
			for (int j = 0; j < m; j++)
				f[i][j] = nextInt();

		g = new ArrayList[n * m + 2];
		S = n * m;
		T = n * m + 1;
		for (int i = 0; i < n * m; i++)
			g[i] = new ArrayList<>(4);
		g[S] = new ArrayList<>(n * m / 2);
		g[T] = new ArrayList<>(n * m / 2);

		for (int i = 0; i < n; i++)
			for (int j = 0; j < m; j++) {
				int v = i * m + j;
				int col = (i + j) & 1;
				if (col == 0) {
					addEdge(S, v, 0);
					for (int dir = 0; dir < 4; dir++) {
						int ni = i + dx[dir];
						int nj = j + dy[dir];
						if (ni >= 0 && ni < n && nj >= 0 && nj < m) {
							int to = ni * m + nj;
							int cost = (f[i][j] == f[ni][nj] ? 0 : 1);
							addEdge(v, to, cost);
						}
					}
				} else {
					addEdge(v, T, 0);
				}
			}

		int ans = 0;
		int[] pi = new int[n * m + 2];

		final int[] d = new int[n * m + 2];
		Edge[] from = new Edge[n * m + 2];

		Integer[] Integers = new Integer[n * m + 2];
		for (int i = 0; i < n * m + 2; i++)
			Integers[i] = i;

		TreeSet<Integer> set = new TreeSet<>(new Comparator<Integer>() {

			@Override
			public int compare(Integer o1, Integer o2) {
				if (d[o1] != d[o2])
					return Integer.compare(d[o1], d[o2]);
				return o1.compareTo(o2);
			}

		});

		for (int iter = 0; iter < n * m / 2; iter++) {

			Arrays.fill(d, INF);
			d[S] = 0;

			for (int i = 0; i < n * m + 2; i++)
				set.add(Integers[i]);

			while (!set.isEmpty()) {
				int v = set.pollFirst();

				for (int i = 0; i < g[v].size(); i++) {
					Edge e = g[v].get(i);
					if (e.flow < e.cap) {
						int redCost = e.cost - pi[v] + pi[e.to];
						if (d[e.to] > d[v] + redCost) {
							set.remove(Integers[e.to]);
							from[e.to] = e;
							d[e.to] = d[v] + redCost;
							set.add(Integers[e.to]);
						}
					}
				}
			}

			for (int i = 0; i < n * m + 2; i++) {
				pi[i] -= d[i];
			}

			for (int v = T; v != S; ) {
				Edge last = from[v];
				last.flow++;
				last.rev.flow--;
				ans += last.cost;
				v = last.rev.to;
			}

		}

		out.println(ans);

	}

	C() throws IOException {
		br = new BufferedReader(new InputStreamReader(System.in));
		out = new PrintWriter(System.out);
		solve();
		out.close();
	}

	public static void main(String[] args) throws IOException {
		new C();
	}

	String nextToken() {
		while (st == null || !st.hasMoreTokens()) {
			try {
				st = new StringTokenizer(br.readLine());
			} catch (Exception e) {
				eof = true;
				return null;
			}
		}
		return st.nextToken();
	}

	String nextString() {
		try {
			return br.readLine();
		} catch (IOException e) {
			eof = true;
			return null;
		}
	}

	int nextInt() throws IOException {
		return Integer.parseInt(nextToken());
	}

	long nextLong() throws IOException {
		return Long.parseLong(nextToken());
	}

	double nextDouble() throws IOException {
		return Double.parseDouble(nextToken());
	}
}


import java.util.*;
public class Abby3_C {
public static void main(String[] args)
{
    Scanner input = new Scanner(System.in);
    int n = input.nextInt(), m = input.nextInt();
    int[][] data = new int[n][m];
    for(int i = 0; i<n; i++)
        for(int j = 0; j<m; j++)
            data[i][j] = input.nextInt();
    MinCostFlow mcf = new MinCostFlow(n*m+2);
    mcf.s = n*m;
    mcf.t = n*m+1;
    int[] di = new int[]{1, 0, -1, 0};
    int[] dj = new int[]{0, 1, 0, -1};
    for(int i = 0; i<n; i++)
    {
        for(int j = 0; j<m; j++)
        {
            if((i+j)%2 == 0)
            {
                mcf.add(mcf.s, i*m+j, 1, 0);
                for(int k = 0; k<4; k++)
                {
                    int ni = i+di[k], nj = j+dj[k];
                    if(ni<0||nj<0||ni>=n||nj>=m) continue;
                    int cost = data[i][j] == data[ni][nj] ? 0 : 1;
                    mcf.add(i*m+j, ni*m+nj, 1, cost);
                }
            }
            else
            {
                mcf.add(i*m+j, mcf.t, 1, 0);
            }
        }
    }
    System.out.println(mcf.minCostFlow()[1]);
}
public static class MinCostFlow{
    int N, s,t;
    ArrayList<Edge>[] adj;
    final int oo = 987654321;
    boolean negCosts;
    int[] pi,dist;
    Edge[] prev;

    public MinCostFlow(int n){
        N = n;
        adj = new ArrayList[N];
        for(int i = 0; i < N; i++)
            adj[i] = new ArrayList<Edge>();
        pi = new int[N];
        prev = new Edge[N];
        dist = new int[N];
    }

    void add(int u, int v, int cap, int cost){
        Edge e = new Edge(u,v,cap,cost);
        negCosts |= (cost < 0);
        Edge r = new Edge(v,u,0,cap,-cost);
        e.rev = r;
        r.rev = e;
        adj[u].add(e);
        adj[v].add(r);
    }

    public boolean dijkstra(){
        Arrays.fill(prev, null);
        Arrays.fill(dist,oo);
        PriorityQueue<Pair> q = new PriorityQueue<Pair>();
        dist[s] = 0;
        q.add(new Pair(s,0));
        while(q.size() > 0){
            Pair p = q.poll();
            int cur = p.first, d = p.second;
            if(dist[cur] < d)
                continue;
            for(Edge e: adj[cur])
                if(e.resid > 0 && dist[e.v] > dist[cur] + e.pcost()){
                    dist[e.v] = dist[cur] + e.pcost();
                    prev[e.v] = e;
                    q.add(new Pair(e.v, dist[e.v]));
                }
        }
        for(int i = 0; i < N; i++)
            if(pi[i] < oo)
                pi[i] += dist[i];
        return prev[t] != null;
    }

    public int[] minCostFlow(){
        int flow = 0, cost = 0;
        if(negCosts){
            Arrays.fill(dist,oo);
            dist[s] = 0;
            for(int k = 0; k < N-1; k++)
                for(int i = 0; i < N; i++)
                    for(Edge e:adj[i])
                        if(e.resid > 0 && dist[e.v] > dist[e.u] + e.cost)
                            dist[e.v] = dist[e.u] + e.cost;
            for(int i = 0; i < N; i++)
                if(pi[i] < oo)
                    pi[i] += dist[i];
        }
        while(dijkstra()){
            int f = Integer.MAX_VALUE/2;
            for(int cur = t; cur != s; cur = prev[cur].u)
                f = Math.min(f, prev[cur].resid);
            for(int cur = t; cur != s; cur = prev[cur].u){
                prev[cur].resid -= f;
                prev[cur].rev.resid += f;
                cost += prev[cur].cost * f;
            }
            flow += f;
        }
        return new int[]{flow,cost};
    }

    public class Edge{
        int u,v,resid,cap,cost;
        Edge rev;
        public Edge(int uu, int vv, int cp, int ct){
            u = uu;
            v = vv;
            resid = cap = cp;
            cost = ct;
        }
        public Edge(int uu, int vv, int rr, int cp, int ct){
            this(uu,vv,cp,ct);
            resid = rr;
        }
        int pcost(){
            return resid > 0?cost+pi[u]-pi[v]:0;
        }
    }
    public class Pair implements Comparable<Pair>{
        int first,second;
        public Pair(int ff, int ss){
            first = ff;
            second = ss;
        }
        public int compareTo(Pair p){
            return second-p.second;
        }
        public String toString(){
            return "("+first +", "+second+")";
        }
    }
}
}


import java.util.List;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Arrays;
import java.util.PriorityQueue;
import java.util.ArrayList;
import java.io.BufferedReader;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.util.StringTokenizer;
import java.util.Queue;
import java.util.Collection;
import java.io.InputStream;

/**
 * Built using CHelper plug-in
 * Actual solution is at the top
 */
public class Main {
	public static void main(String[] args) {
		InputStream inputStream = System.in;
		OutputStream outputStream = System.out;
		InputReader in = new InputReader(inputStream);
		PrintWriter out = new PrintWriter(outputStream);
		TaskC2 solver = new TaskC2();
		solver.solve(1, in, out);
		out.close();
	}
}

class TaskC2 {
	static class Edge {
		int to, f, cap, cost, rev;

		Edge(int v, int cap, int cost, int rev) {
			this.to = v;
			this.cap = cap;
			this.cost = cost;
			this.rev = rev;
		}
	}

	public static List<Edge>[] createGraph(int n) {
		List<Edge>[] graph = new List[n];
		for (int i = 0; i < n; i++)
			graph[i] = new ArrayList<Edge>();
		return graph;
	}

	public static void addEdge(List<Edge>[] graph, int s, int t, int cap, int cost) {
		graph[s].add(new Edge(t, cap, cost, graph[t].size()));
		graph[t].add(new Edge(s, 0, -cost, graph[s].size() - 1));
	}

	static void bellmanFord(List<Edge>[] graph, int s, int[] dist) {
		int n = graph.length;
		Arrays.fill(dist, 0, n, Integer.MAX_VALUE);
		dist[s] = 0;
		boolean[] inqueue = new boolean[n];
		int[] q = new int[n];
		int qt = 0;
		q[qt++] = s;
		for (int qh = 0; (qh - qt) % n != 0; qh++) {
			int u = q[qh % n];
			inqueue[u] = false;
			for (int i = 0; i < (int) graph[u].size(); i++) {
				Edge e = graph[u].get(i);
				if (e.cap <= e.f)
					continue;
				int v = e.to;
				int ndist = dist[u] + e.cost;
				if (dist[v] > ndist) {
					dist[v] = ndist;
					if (!inqueue[v]) {
						inqueue[v] = true;
						q[qt++ % n] = v;
					}
				}
			}
		}
	}

	static int[] minCostFlow(List<Edge>[] graph, int s, int t, int maxf) {
		int n = graph.length;
		int[] prio = new int[n];
		int[] curflow = new int[n];
		int[] prevedge = new int[n];
		int[] prevnode = new int[n];
		int[] pot = new int[n];

		// bellmanFord can be safely commented if edges costs are non-negative
		bellmanFord(graph, s, pot);
		int flow = 0;
		int flowCost = 0;
		while (flow < maxf) {
			Queue<Long> q = new PriorityQueue<Long>();
			q.add((long) s);
			Arrays.fill(prio, 0, n, Integer.MAX_VALUE);
			prio[s] = 0;
			curflow[s] = Integer.MAX_VALUE;
			while (!q.isEmpty()) {
				long cur = q.remove();
				int d = (int) (cur >>> 32);
				int u = (int) (cur & 0x7FFFFFFF);
				if (d != prio[u])
					continue;
				for (int i = 0; i < graph[u].size(); i++) {
					Edge e = graph[u].get(i);
					int v = e.to;
					if (e.cap <= e.f)
						continue;
					int nprio = prio[u] + e.cost + pot[u] - pot[v];
					if (prio[v] > nprio) {
						prio[v] = nprio;
						q.add(((long) nprio << 32) + v);
						prevnode[v] = u;
						prevedge[v] = i;
						curflow[v] = Math.min(curflow[u], e.cap - e.f);
					}
				}
			}
			if (prio[t] == Integer.MAX_VALUE)
				break;
			for (int i = 0; i < n; i++)
				pot[i] += prio[i];
			int df = Math.min(curflow[t], maxf - flow);
			flow += df;
			for (int v = t; v != s; v = prevnode[v]) {
				Edge e = graph[prevnode[v]].get(prevedge[v]);
				e.f += df;
				graph[v].get(e.rev).f -= df;
				flowCost += df * e.cost;
			}
		}
		return new int[]{flow, flowCost};
	}

	public void solve(int testNumber, InputReader in, PrintWriter out) {
		int n = in.nextInt();
		int m = in.nextInt();
		int[][] a = new int[n][m];
		for (int i = 0; i < n; i++) {
			for (int j = 0; j < m; j++) {
				a[i][j] = in.nextInt();
			}
		}

		final List<Edge>[] g = createGraph(n * m + 2);
		int s = n * m;
		int t = s + 1;
		for (int i = 0; i < n; i++) {
			for (int j = 0; j < m; j++) {
				if ((i + j) % 2 == 1) {
					addEdge(g, s, i * m + j, 1, 0);
					int dx[] = {1, -1, 0, 0};
					int dy[] = {0, 0, 1, -1};
					for (int k = 0; k < 4; k++) {
						int ni = i + dx[k];
						int nj = j + dy[k];

						if (0 <= ni && ni < n && 0 <= nj && nj < m) {
							addEdge(g, i * m + j, ni * m + nj, 1, a[i][j] != a[ni][nj] ? 1 : 0);
						}
					}
				} else {
					addEdge(g, i * m + j, t, 1, 0);
				}
			}
		}
		int[] res = minCostFlow(g, s, t, Integer.MAX_VALUE);
		int flow = res[0];
		int flowCost = res[1];
		out.println(flowCost);
	}
}

class InputReader {
	public BufferedReader reader;
	public StringTokenizer tokenizer;

	public InputReader(InputStream stream) {
		reader = new BufferedReader(new InputStreamReader(stream));
	}

	public String next() {
		while (tokenizer == null || !tokenizer.hasMoreTokens()) {
			try {
				tokenizer = new StringTokenizer(reader.readLine());
			} catch (IOException e) {
				throw new RuntimeException(e);
			}
		}
		return tokenizer.nextToken();
	}

	public int nextInt() {
		return Integer.parseInt(next());
	}
}


import java.io.*;
import java.util.*;

public class Main implements Runnable {

	public void _main() throws IOException {
		int height = nextInt();
		int width = nextInt();
		int[][] a = new int[height][width];
		for (int r = 0; r < height; r++) {
			for (int c = 0; c < width; c++) {
				a[r][c] = nextInt();
			}
		}
		Graph g = new Graph(height * width + 2);
		for (int r = 0; r < height; r++) {
			for (int c = 0; c < width; c++) {
				if ((r + c) % 2 == 0) {
					g.addEdge(g.s, r * width + c, 1, 0, -1);
				} else {
					g.addEdge(r * width + c, g.t, 1, 0, -1);
					continue;
				}
				for (int dr = -1; dr <= 1; dr++) {
					for (int dc = -1; dc <= 1; dc++) {
						if (dr == 0 ^ dc == 0) {
							int nr = r + dr;
							int nc = c + dc;
							if (nr < 0 || nr >= height || nc < 0 || nc >= width) continue;
							int cost = a[r][c] == a[nr][nc] ? 0 : 1;
							g.addEdge(r * width + c, nr * width + nc, 1, cost, -1);
						}
					}
				}
			}
		}
		out.println(g.minCostFlow());
	}

	class Graph {
		final int infinity = 1500000000;
		int n, s, t;

		final int N = 80 * 80 + 100;
		final int M = 8 * N;
		int[] firstEdge = new int[N];
		{
			Arrays.fill(firstEdge, -1);
		}
		int[] edgeFrom = new int[M];
		int[] edgeTo = new int[M];
		int[] edgeCap = new int[M];
		int[] edgeCost = new int[M];
		int[] edgeFlow = new int[M];
		int[] nextEdge = new int[M];
		int[] edgeId = new int[M];
		int numEdges;

		int[] heap = new int[N];
		int[] heapWhere = new int[N];
		int[] dist = new int[N];
		int heapSize;

		int[] prev = new int[N];
		int[] phi = new int[N];

		Graph(int n) {
			this.n = n;
			s = n - 1;
			t = n - 2;
		}
		void addEdge(int from, int to, int cap, int cost, int id) {
			int e;

			e = numEdges++;
			nextEdge[e] = firstEdge[from];
			firstEdge[from] = e;
			edgeFrom[e] = from;
			edgeTo[e] = to;
			edgeCap[e] = cap;
			edgeCost[e] = cost;
			edgeId[e] = id;

			e = numEdges++;
			nextEdge[e] = firstEdge[to];
			firstEdge[to] = e;
			edgeFrom[e] = to;
			edgeTo[e] = from;
			edgeCap[e] = 0;
			edgeCost[e] = -cost;
			edgeId[e] = id;
		}
		int minCostFlow() {
			/*
			for (int step = 0; step < n - 1; step++) {
				for (int i = 0; i < n; i++) {
					for (int e = firstEdge[i]; e >= 0; e = nextEdge[e]) {
						int j = edgeTo[e];
						if (phi[j] > phi[i] + edgeCost[e]) {
							phi[j] = phi[i] + edgeCost[e];
						}
					}
				}
			}
			*/
			int flow = 0;
			int flowCost = 0;
			Arrays.fill(heapWhere, 0, n, -1);
			while (true) {
				Arrays.fill(prev, 0, n, -1);
				Arrays.fill(dist, 0, n, infinity);
				dist[s] = 0;
				heapSize = 1;
				heapWhere[s] = 0;
				heap[0] = s;
				while (heapSize > 0) {
					int v = heap[0];
					if (heapSize > 1) {
						heapSwap(0, heapSize - 1);
						--heapSize;
						heapWhere[v] = -1;
						heapDown(0);
					} else {
						--heapSize;
						heapWhere[v] = -1;
					}
					for (int e = firstEdge[v]; e >= 0; e = nextEdge[e]) {
						if (edgeFlow[e] >= edgeCap[e]) continue;
						int u = edgeTo[e];
						if (dist[u] > dist[v] + edgeCost[e] + phi[v] - phi[u]) {
							dist[u] = dist[v] + edgeCost[e] + phi[v] - phi[u];
							prev[u] = e;
							if (heapWhere[u] < 0) {
								heapWhere[u] = heapSize++;
								heap[heapSize - 1] = u;
							}
							heapUp(heapWhere[u]);
							heapDown(heapWhere[u]);
						}
					}
				}
				if (prev[t] < 0) {
					break;
				}
				int dCost = dist[t] + phi[t] - phi[s];
				int v = t;
				while (v != s) {
					int e = prev[v];
					++edgeFlow[e];
					--edgeFlow[e ^ 1];
					v = edgeFrom[e];
				}
				++flow;
				flowCost += dCost;
				for (int i = 0; i < n; i++) {
					if (dist[i] < infinity) {
						phi[i] += dist[i];
					}
				}
			}
			return flowCost;
		}
		private void heapUp(int i) {
			while (i > 0) {
				int p = (i - 1) / 2;
				if (dist[i] < dist[p]) {
					heapSwap(i, p);
					i = p;
				} else {
					break;
				}
			}
		}
		private void heapDown(int i) {
			while (true) {
				int j = i;
				if (2*i + 1 < heapSize && dist[2*i + 1] < dist[j]) {
					j = 2*i + 1;
				}
				if (2*i + 2 < heapSize && dist[2*i + 2] < dist[j]) {
					j = 2*i + 2;
				}
				if (i == j) {
					break;
				} else {
					heapSwap(i, j);
					i = j;
				}
			}
		}
		private void heapSwap(int i, int j) {
			int ai = heap[i];
			int aj = heap[j];
			heapWhere[ai] = j;
			heapWhere[aj] = i;
			heap[i] = aj;
			heap[j] = ai;
		}

	}

	private BufferedReader in;
	private PrintWriter out;
	private StringTokenizer st;

	private String next() throws IOException {
		while (st == null || !st.hasMoreTokens()) {
			String rl = in.readLine();
			if (rl == null)
				return null;
			st = new StringTokenizer(rl);
		}
		return st.nextToken();
	}

	private int nextInt() throws IOException {
		return Integer.parseInt(next());
	}

	private long nextLong() throws IOException {
		return Long.parseLong(next());
	}

	private double nextDouble() throws IOException {
		return Double.parseDouble(next());
	}

	public static void main(String[] args) {
		Locale.setDefault(Locale.UK);
		new Thread(new Main()).start();
	}

	public void run() {
		try {
			in = new BufferedReader(new InputStreamReader(System.in));
			out = new PrintWriter(System.out);
			//in = new BufferedReader(new FileReader("a.in"));
			//out = new PrintWriter(new FileWriter("a.out"));

			_main();

			out.close();
		} catch (Exception e) {
			e.printStackTrace();
			System.exit(202);
		}
	}

}


//package abbyy3;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.util.Arrays;
import java.util.InputMismatchException;

public class C {
	InputStream is;
	PrintWriter out;
	String INPUT = "";

	void solve()
	{
		int n = ni(), m = ni();
		int[][] map = new int[n][m];
		for(int i = 0;i < n;i++){
			for(int j = 0;j < m;j++){
				map[i][j] = ni();
			}
		}

		int[][] dp = new int[n][1<<m];
		for(int i = 0;i < n;i++){
			Arrays.fill(dp[i], -99999);
		}
		dp[0] = makeRow(map[0]);
		for(int i = 1;i < n;i++){
			int[] row = makeRow(map[i]);
			for(int j = 0;j < 1<<m;j++){
				for(int k = 0;k < 1<<m;k++){ // k->j
					int inv = (~k&(1<<m)-1);
					if((inv&j) == inv){
						int nex = row[j^inv] + dp[i-1][k];
						for(int l = 0;l < m;l++){
							if(inv<<31-l<0 && map[i-1][l] == map[i][l]){
								nex++;
							}
						}
						dp[i][j] = Math.max(dp[i][j], nex);
					}
				}
			}
		}
		out.println(n*m/2-dp[n-1][(1<<m)-1]);
	}

	int[] makeRow(int[] r)
	{
		int m = r.length;
		int[] row = new int[1<<m];
		Arrays.fill(row, -99999);
		row[0] = 0;
		for(int i = 0;i < 1<<m;i++){
			for(int j = 3, k = 0;j < 1<<m;j*=2, k++){
				if((i&j) == j){
					if(row[i^j] != -99999){
						row[i] = row[i^j] + (r[k] == r[k+1] ? 1 : 0);
					}
					break;
				}
			}
		}
		return row;
	}

	void run() throws Exception
	{
		is = oj ? System.in : new ByteArrayInputStream(INPUT.getBytes());
		out = new PrintWriter(System.out);

		long s = System.currentTimeMillis();
		solve();
		out.flush();
		tr(System.currentTimeMillis()-s+"ms");
	}

	public static void main(String[] args) throws Exception { new C().run(); }

	private byte[] inbuf = new byte[1024];
	private int lenbuf = 0, ptrbuf = 0;

	private int readByte()
	{
		if(lenbuf == -1)throw new InputMismatchException();
		if(ptrbuf >= lenbuf){
			ptrbuf = 0;
			try { lenbuf = is.read(inbuf); } catch (IOException e) { throw new InputMismatchException(); }
			if(lenbuf <= 0)return -1;
		}
		return inbuf[ptrbuf++];
	}

	private boolean isSpaceChar(int c) { return !(c >= 33 && c <= 126); }
	private int skip() { int b; while((b = readByte()) != -1 && isSpaceChar(b)); return b; }

	private double nd() { return Double.parseDouble(ns()); }
	private char nc() { return (char)skip(); }

	private String ns()
	{
		int b = skip();
		StringBuilder sb = new StringBuilder();
		while(!(isSpaceChar(b))){ // when nextLine, (isSpaceChar(b) && b != ' ')
			sb.appendCodePoint(b);
			b = readByte();
		}
		return sb.toString();
	}

	private char[] ns(int n)
	{
		char[] buf = new char[n];
		int b = skip(), p = 0;
		while(p < n && !(isSpaceChar(b))){
			buf[p++] = (char)b;
			b = readByte();
		}
		return n == p ? buf : Arrays.copyOf(buf, p);
	}

	private char[][] nm(int n, int m)
	{
		char[][] map = new char[n][];
		for(int i = 0;i < n;i++)map[i] = ns(m);
		return map;
	}

	private int[] na(int n)
	{
		int[] a = new int[n];
		for(int i = 0;i < n;i++)a[i] = ni();
		return a;
	}

	private int ni()
	{
		int num = 0, b;
		boolean minus = false;
		while((b = readByte()) != -1 && !((b >= '0' && b <= '9') || b == '-'));
		if(b == '-'){
			minus = true;
			b = readByte();
		}

		while(true){
			if(b >= '0' && b <= '9'){
				num = num * 10 + (b - '0');
			}else{
				return minus ? -num : num;
			}
			b = readByte();
		}
	}

	private long nl()
	{
		long num = 0;
		int b;
		boolean minus = false;
		while((b = readByte()) != -1 && !((b >= '0' && b <= '9') || b == '-'));
		if(b == '-'){
			minus = true;
			b = readByte();
		}

		while(true){
			if(b >= '0' && b <= '9'){
				num = num * 10 + (b - '0');
			}else{
				return minus ? -num : num;
			}
			b = readByte();
		}
	}

	private boolean oj = System.getProperty("ONLINE_JUDGE") != null;
	private void tr(Object... o) { if(!oj)System.out.println(Arrays.deepToString(o)); }
}


import java.io.*;
import java.util.*;

public class C {

	static void solve() throws IOException {
		int n = nextInt(), m = nextInt();
		int[][] what = new int[n][m];
		for (int i = 0; i < n; i++) {
			for (int j = 0; j < m; j++) {
				what[i][j] = nextInt();
			}
		}
		Graph g = new Graph(n * m + 2);
		int[] dx = { 0, 0, 1, -1 };
		int[] dy = { 1, -1, 0, 0 };
		int start = n * m;
		int finish = n * m + 1;
		for (int i = 0; i < n; i++) {
			for (int j = 0; j < m; j++) {
				int from = i * m + j;
				if (((i ^ j) & 1) != 0) {
					g.addEdge(from, finish, 1, 0);
					continue;
				} else {
					g.addEdge(start, from, 1, 0);
				}
				for (int dir = 0; dir < 4; dir++) {
					int ni = i + dx[dir];
					int nj = j + dy[dir];
					if (ni < 0 || ni >= n || nj < 0 || nj >= m) {
						continue;
					}
					int to = ni * m + nj;
					int cost = 1;
					if (what[i][j] == what[ni][nj]) {
						cost = 0;
					}
					g.addEdge(from, to, 1, cost);
				}
			}
		}
		long cost = g.minCostFlow(start, finish)[1];
		out.println(cost);
	}

	public static class QItem implements Comparable<QItem> {
		long d;
		int v;

		public QItem(long d, int v) {
			this.d = d;
			this.v = v;
		}

		public int compareTo(QItem q) {
			return d < q.d ? -1 : d > q.d ? 1 : 0;
		}
	}

	public static class Graph {
		public final int n;
		public List<Edge>[] edges;

		@SuppressWarnings("unchecked")
		public Graph(int n) {
			this.n = n;
			edges = new List[n];
			for (int i = 0; i < n; i++) {
				edges[i] = new ArrayList<Edge>();
			}
		}

		public void addEdge(int u, int v, int cap, int cost) {
			Edge e1 = new Edge(v, cap, cost);
			Edge e2 = new Edge(u, 0, -cost);
			e1.rev = e2;
			e2.rev = e1;
			edges[u].add(e1);
			edges[v].add(e2);
		}

		public void addUndirectedEdge(int u, int v, int cap, int cost) {
			Edge e1 = new Edge(v, cap, cost);
			Edge e2 = new Edge(u, cap, -cost);
			e1.rev = e2;
			e2.rev = e1;
			edges[u].add(e1);
			edges[v].add(e2);
		}

		public void clearFlow() {
			for (List<Edge> edgesList : edges) {
				for (Edge e : edgesList) {
					e.flow = 0;
				}
			}
		}

		// input: s - source, t - sink
		// output: maximum flow and minimum cost
		public long[] minCostFlow(int s, int t) {
			final long INF = 1L << 50;
			long flow = 0;
			long flowCost = 0;
			long[] u = new long[n];
			while (true) {
				Queue<QItem> q = new PriorityQueue<QItem>();
				q.add(new QItem(0, s));
				Edge[] p = new Edge[n];
				long[] d = new long[n];
				Arrays.fill(d, INF);
				d[s] = 0;
				while (!q.isEmpty()) {
					QItem cur = q.poll();
					if (cur.d != d[cur.v]) {
						continue;
					}
					for (Edge e : edges[cur.v]) {
						long nd = d[cur.v] + e.cost + u[cur.v] - u[e.to];
						if (e.cap > e.flow && d[e.to] > nd) {
							d[e.to] = nd;
							p[e.to] = e;
							q.add(new QItem(nd, e.to));
						}
					}
				}
				if (p[t] == null) {
					break;
				}
				for (int i = 0; i < n; i++) {
					if (p[i] != null) {
						u[i] += d[i];
					}
				}
				int min = Integer.MAX_VALUE;
				for (Edge e = p[t]; e != null; e = p[e.rev.to]) {
					min = Math.min(min, e.cap - e.flow);
				}

				flow += min;
				for (Edge e = p[t]; e != null; e = p[e.rev.to]) {
					e.flow += min;
					e.rev.flow -= min;
					flowCost += (long) min * e.cost;
				}
			}
			return new long[] { flow, flowCost };
		}
	}

	public static class Edge {
		public int to, cap, flow, cost;
		public Edge rev;

		public Edge(int to, int cap, int cost) {
			this.to = to;
			this.cap = cap;
			this.cost = cost;
		}
	}

	static BufferedReader br;
	static StringTokenizer st;
	static PrintWriter out;

	public static void main(String[] args) throws IOException {
		InputStream input = System.in;
		PrintStream output = System.out;
		File file = new File("c.in");
		if (file.exists() && file.canRead()) {
			input = new FileInputStream(file);
		}
		br = new BufferedReader(new InputStreamReader(input));
		out = new PrintWriter(output);
		solve();
		out.close();
	}

	static long nextLong() throws IOException {
		return Long.parseLong(nextToken());
	}

	static double nextDouble() throws IOException {
		return Double.parseDouble(nextToken());
	}

	static int nextInt() throws IOException {
		return Integer.parseInt(nextToken());
	}

	static String nextToken() throws IOException {
		while (st == null || !st.hasMoreTokens()) {
			String line = br.readLine();
			if (line == null) {
				return null;
			}
			st = new StringTokenizer(line);
		}
		return st.nextToken();
	}
}


import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.InputMismatchException;

public class A {
	static int[] dx = {0,0,1,-1};
	static int[] dy = {1,-1,0,0};
	static int N,M;
	public static void main(String[] args) {
		MScanner sc = new MScanner();
		PrintWriter out = new PrintWriter(System.out);
		N = sc.nextInt();
		M = sc.nextInt();
		int[][] grid = sc.nextInt(N,M);
		int NM = N*M;
		MinCostFlow MCF = new MinCostFlow(NM);
		int pos = 0;
		for(int a=0;a<N;a++){
			for(int b=0;b<M;b++){
				if((a+b)%2==0){
					MCF.add(MCF.s, pos, 1, 0);
					for(int d=0;d<4;d++){
						int ny = dy[d]+a;
						int nx = dx[d]+b;
						if(ny<0||ny>=N||nx<0||nx>=M)
							continue;
						if(grid[a][b]==grid[ny][nx])MCF.add(pos, magic(ny,nx), 1, 0);
						else MCF.add(pos, magic(ny,nx), 1, 1);
					}
				}
				else if((a+b)%2==1)MCF.add(pos, MCF.t, 1, 0);
				pos++;
			}
		}
		out.println(MCF.getFlow()[1]);
		out.close();
	}

	private static int magic(int ny, int nx) {
		return ny*M + nx;
	}

	static class MinCostFlow {
		  public static int MINCOSTFLOW = 0, MINCOSTMAXFLOW = 1;
		  public int s, t;
		  int N, ss, tt;
		  public long oo = (long) 1e12;
		  long[] ex;
		  ArrayList<Edge>[] adjj;
		  public Edge[][] adj;

		  public MinCostFlow(int NN) {
		    this(NN, MINCOSTMAXFLOW);
		  }

		  public MinCostFlow(int NN, int flowType) {
		    N = (tt = (ss = (t = (s = NN) + 1) + 1) + 1) + 1;
		    adj = new Edge[N][0];
		    adjj = new ArrayList[N];
		    for (int i = 0; i < N; i++)
		      adjj[i] = new ArrayList<Edge>();
		    ex = new long[N];
		    add(t, s, oo, -oo / 10 * flowType);
		  }

		  public void add(int i, int j, long cap, long cost) {
		    Edge fwd = new Edge(i, j, cap, 0, cost);
		    Edge rev = new Edge(j, i, 0, 0, -cost);
		    adjj[i].add(rev.rev = fwd);
		    adjj[j].add(fwd.rev = rev);
		  }

		  public long[] getFlow() {
		    preFlow();
		    for (int i = 0; i < N; i++)
		      adj[i] = adjj[i].toArray(adj[i]);
		    boolean[] canU = new boolean[N], hasU = new boolean[N];
		    long[] dist = new long[N], width = new long[N];
		    Edge[] prev = new Edge[N];
		    while (true) {
		      Arrays.fill(dist, oo);
		      dist[ss] = 0;
		      width[ss] = oo;
		      boolean updated = hasU[ss] = true;
		      while (updated) {
		        updated = false;
		        for (int i = 0; i < N; hasU[i++] = false)
		          canU[i] = hasU[i];
		        for (int i = 0; i < N; i++)
		          if (canU[i])
		            for (Edge e : adj[i])
		              if (e.flow != e.cap && dist[e.j] > dist[e.i] + e.cost) {
		                dist[e.j] = dist[e.i] + (prev[e.j] = e).cost;
		                width[e.j] = Math.min(width[e.i], e.cap - e.flow);
		                hasU[e.j] = updated = true;
		              }
		      }
		      if (dist[tt] >= oo)
		        break;
		      for (Edge e = prev[tt]; e != null; e = prev[e.i])
		        e.rev.flow = -(e.flow += width[tt]);
		    }
		    long flow = 0, cost = 0;
		    for (Edge e : adj[s])
		      if (e.flow > 0)
		        flow += e.flow;
		    for (int i = 0; i < N; i++)
		      for (Edge e : adj[i])
		        if (e.flow > 0 && e.i != t && e.j != s && e.i < ss && e.j < ss)
		          cost += e.flow * e.cost;
		    return new long[] { flow, cost };
		  }

		  public void preFlow() {
		    for (int i = 0; i < N; i++)
		      for (Edge e : adjj[i])
		        if (e.cost < 0 && e.cap - e.flow > 0) {
		          ex[e.i] -= e.cap - e.flow;
		          ex[e.j] += e.cap - e.flow;
		          e.rev.flow = -(e.flow = e.cap);
		        }
		    for (int i = 0; i < N; i++)
		      if (ex[i] > 0)
		        add(ss, i, ex[i], -oo);
		      else if (ex[i] < 0)
		        add(i, tt, -ex[i], -oo);
		    Arrays.fill(ex, 0);
		  }

		  public class Edge {
		    public int i, j;
		    public long cap, flow, cost;
		    Edge rev;

		    Edge(int ii, int jj, long cc, long ff, long C) {
		      i = ii;
		      j = jj;
		      cap = cc;
		      flow = ff;
		      cost = C;
		    }
		  }
		}

	static class MScanner{
		private InputStream stream;
		private byte[] buf = new byte[1024];
		private int curChar;
		private int numChars;

		public MScanner(){
			stream = System.in;
			//stream = new FileInputStream(new File("dec.in"));

		}
		int read(){
			if(numChars==-1)
				throw new InputMismatchException();
			if(curChar>=numChars){
				curChar = 0;
				try{
					numChars = stream.read(buf);
				} catch (IOException e){
					throw new InputMismatchException();
				}
				if(numChars <= 0)
					return -1;
			}
			return buf[curChar++];
		}

		boolean isSpaceChar(int c){
			return c==' '||c=='\n'||c=='\r'||c=='\t'||c==-1;
		}
		boolean isEndline(int c){
			return c=='\n'||c=='\r'||c==-1;
		}
		int nextInt(){
			return Integer.parseInt(next());
		}
		int[] nextInt(int N){
			int[] ret = new int[N];
			for(int a=0;a<N;a++)
				ret[a] = nextInt();
			return ret;
		}
		int[][] nextInt(int N, int M){
			int[][] ret = new int[N][M];
			for(int a=0;a<N;a++)
				ret[a] = nextInt(M);
			return ret;
		}
		long nextLong(){
			return Long.parseLong(next());
		}
		long[] nextLong(int N){
			long[] ret = new long[N];
			for(int a=0;a<N;a++)
				ret[a] = nextLong();
			return ret;
		}
		double nextDouble(){
			return Double.parseDouble(next());
		}
		double[] nextDouble(int N){
			double[] ret = new double[N];
			for(int a=0;a<N;a++)
				ret[a] = nextDouble();
			return ret;
		}
		String next(){
			int c = read();
			while(isSpaceChar(c))
				c=read();
			StringBuilder res = new StringBuilder();
			do{
				res.appendCodePoint(c);
				c=read();
			} while(!isSpaceChar(c));
			return res.toString();
		}
		String[] next(int N){
			String[] ret = new String[N];
			for(int a=0;a<N;a++)
				ret[a] = next();
			return ret;
		}
		String nextLine(){
			int c = read();
			while(isEndline(c))
				c=read();
			StringBuilder res = new StringBuilder();
			do{
				res.appendCodePoint(c);
				c = read();
			}while(!isEndline(c));
			return res.toString();
		}
		String[] nextLine(int N){
			String[] ret = new String[N];
			for(int a=0;a<N;a++)
				ret[a] = nextLine();
			return ret;
		}

	}
}


import java.math.BigDecimal;
import java.util.Scanner;

public class Main {
    static BigDecimal pow(BigDecimal a, BigDecimal b) {
        double bDouble = b.doubleValue();
        int integerPortion = (int) bDouble;
        double fractionalPortion = bDouble - integerPortion;
        BigDecimal aToIntegerPortion = a.pow(integerPortion);
        BigDecimal aToFractionalPortion = new BigDecimal(Math.pow(a.doubleValue(), fractionalPortion));
        return aToIntegerPortion.multiply(aToFractionalPortion);
    }

    static BigDecimal log(BigDecimal value) {
        return new BigDecimal(Math.log(value.doubleValue()));
    }

    public static void main (String[] args) {
        Scanner sc = new Scanner(System.in);
        BigDecimal x = new BigDecimal(sc.next());
        BigDecimal y = new BigDecimal(sc.next());
        BigDecimal z = new BigDecimal(sc.next());
        sc.close();

        BigDecimal[] logValues = {
                pow(y, z).multiply(log(x)),
                pow(z, y).multiply(log(x)),
                z.multiply(y).multiply(log(x)),
                y.multiply(z).multiply(log(x)),
                pow(x, z).multiply(log(y)),
                pow(z, x).multiply(log(y)),
                z.multiply(x).multiply(log(y)),
                x.multiply(z).multiply(log(y)),
                pow(x, y).multiply(log(z)),
                pow(y, x).multiply(log(z)),
                y.multiply(x).multiply(log(z)),
                x.multiply(y).multiply(log(z))
        };

        int greatestIndex = 0;
        for (int i = 1; i < logValues.length; i++) {
            if (logValues[i].compareTo(logValues[greatestIndex]) > 0) greatestIndex = i;
        }

        String[] valueRepresentations = {
                "x^y^z",
                "x^z^y",
                "(x^y)^z",
                "(x^z)^y",
                "y^x^z",
                "y^z^x",
                "(y^x)^z",
                "(y^z)^x",
                "z^x^y",
                "z^y^x",
                "(z^x)^y",
                "(z^y)^x"
        };

        System.out.print(valueRepresentations[greatestIndex]);
    }
}


// package QS.AA.NetworkFlow;

// B. Help General
// https://codeforces.com/problemset/problem/142/B
import java.util.*;
import java.io.*;

public class CF_142B {
	public static void main(String[] args) throws IOException {
		java.io.BufferedReader br = new java.io.BufferedReader(new java.io.InputStreamReader(System.in));
		String[] tokens = br.readLine().trim().split("\\s");
		int n = Integer.parseInt(tokens[0]);
		int m = Integer.parseInt(tokens[1]);
		if (n > m) {int temp = n; n = m; m = temp;}
		int ans = 0;
		if (n == 1 || m == 1) {
			ans = m * n;
		} else if (n == 2 && m >= 2) {
			ans = m - (m % 4) + Math.min(m % 4, 2) * 2;
		} else {
			ans = (m * n + 1) / 2;
		}
		System.out.println(ans);
	}
}


import java.util.Scanner;
import java.util.Arrays;
import java.util.*;
import java.lang.*;

public class Code {

	public static void main(String[] args) {
		Scanner cin = new Scanner(System.in);
		ArrayList<Integer> list = new ArrayList<>();
		HashSet<Integer> hash = new HashSet<>();
		int result =  0;
		int n = cin.nextInt();
		cin.nextLine();

		int[] arr = new int[n];

		for(int i = 0;i<n;i++) {
			arr[i] = cin.nextInt();
		}
					if(n ==1 ) {
						System.out.println(arr[0]);
						return;
					}

		Arrays.sort(arr);

		System.out.println(arr[n/2]);

		cin.close();
	}
}


import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Locale;
import java.util.PriorityQueue;
import java.util.Random;
import java.util.StringTokenizer;

public class C {

	private void solve() throws IOException {
		long n = nextLong();

		if (n % 3 != 0) {
			println("0");
			return;
		}

		n /= 3;

		int res = 0;
		for (long i = 1; i * i * i <= n; i++) {
			if (n % i != 0) {
				continue;
			}

			for (long a = 1; a * a <= n && a + a <= i; a++) {
				long b = i - a;
				long m = n / i;
				long d = i * i - 4 * (a * b - m);

				if (d <= 0) {
					continue;
				} else {
					long sq = (long)Math.sqrt(d);
					if (sq * sq != d || sq < i || (sq - i) % 2 == 1) {
						continue;
					}

					long c = (sq - i) / 2;
					if (c < a || c < b) {
						continue;
					}

					if (a == b && a == c) {
						res++;
					} else if (a != b && a != c && b != c) {
						res += 6;
					} else {
						res += 3;
					}
				}
			}
		}

		println(res);
	}

	private String nextToken() throws IOException {
		while (tokenizer == null || !tokenizer.hasMoreTokens()) {
			tokenizer = new StringTokenizer(reader.readLine());
		}
		return tokenizer.nextToken();
	}

	private int nextInt() throws NumberFormatException, IOException {
		return Integer.parseInt(nextToken());
	}

	private double nextDouble() throws NumberFormatException, IOException {
		return Double.parseDouble(nextToken());
	}

	private long nextLong() throws IOException {
		return Long.parseLong(nextToken());
	}

	private void print(Object o) {
		writer.print(o);
	}

	private void println(Object o) {
		writer.println(o);
	}

	private void printf(String format, Object... o) {
		writer.printf(format, o);
	}

	public static void main(String[] args) {
		long time = System.currentTimeMillis();
		Locale.setDefault(Locale.US);
		new C().run();
		System.err.printf("%.3f\n", 1e-3 * (System.currentTimeMillis() - time));
	}

	BufferedReader reader;
	StringTokenizer tokenizer;
	PrintWriter writer;

	private void run() {
		try {
			reader = new BufferedReader(new InputStreamReader(System.in));
			writer = new PrintWriter(System.out);
			solve();
			reader.close();
			writer.close();
		} catch (IOException e) {
			e.printStackTrace();
			System.exit(13);
		}
	}
}


import java.util.Scanner;

public class Future {

	public static void main(String[] args) {
		Scanner in = new Scanner(System.in);
		long n = in.nextLong();
		long a = in.nextLong();
		long b = in.nextLong();
		long c = in.nextLong();
		long k = 0;
		if (b - c < a && n >= b) {
			k = (n-b)/(b-c) + 1;
			n -= k * (b-c);
			k += n/a;
		}
		else if(n < b || b - c >= a) {
			k = n/a;
		}
		System.out.println(k);
		in.close();
	}

}


import java.io.InputStreamReader;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.BufferedWriter;
import java.io.BufferedReader;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.io.Writer;
import java.util.StringTokenizer;
import java.io.InputStream;

/**
 * Built using CHelper plug-in
 * Actual solution is at the top
 * @author Lokesh Khandelwal
 */
public class Main {
	public static void main(String[] args) {
		InputStream inputStream = System.in;
		OutputStream outputStream = System.out;
		InputReader in = new InputReader(inputStream);
		OutputWriter out = new OutputWriter(outputStream);
		TaskB solver = new TaskB();
		solver.solve(1, in, out);
		out.close();
	}
}

class TaskB {
    public void solve(int testNumber, InputReader in, OutputWriter out) {
        int n = in.nextInt(), d = in.nextInt(), i, a[] = new int[n], total = 0, j;
        for(i=0;i<n;i++) {
            a[i] = in.nextInt();
            total += a[i];
        }
        boolean canSum[] = new boolean[total+1];
        canSum[0] = true;
        for(i=0;i<n;i++) {
            for(j=total;j>=a[i];j--) {
                if(canSum[j - a[i]])
                    canSum[j] = true;
            }
        }
        int max = 0, ans = 0;
        while (true) {
            int nmax = max;
            for(i=max+1;i<=total && i<=max+d;i++) {
                if(canSum[i])
                    nmax = i;
            }
            if(nmax == max)
                break;
            ans++;
            max = nmax;
        }
        out.printLine(max +" "+ans);
    }
}

class InputReader
{
    BufferedReader in;
    StringTokenizer tokenizer=null;

    public InputReader(InputStream inputStream)
    {
        in=new BufferedReader(new InputStreamReader(inputStream));
    }
    public String next()
    {
        try{
            while (tokenizer==null||!tokenizer.hasMoreTokens())
            {
                tokenizer=new StringTokenizer(in.readLine());
            }
            return tokenizer.nextToken();
        }
        catch (IOException e)
        {
            return null;
        }
    }
    public int nextInt()
    {
        return Integer.parseInt(next());
    }
    }

class OutputWriter {
	private final PrintWriter writer;

	public OutputWriter(OutputStream outputStream) {
		writer = new PrintWriter(new BufferedWriter(new OutputStreamWriter(outputStream)));
	}

	public OutputWriter(Writer writer) {
		this.writer = new PrintWriter(writer);
	}

	public void print(Object...objects) {
		for (int i = 0; i < objects.length; i++) {
			if (i != 0)
				writer.print(' ');
			writer.print(objects[i]);
		}
	}

	public void printLine(Object...objects) {
		print(objects);
		writer.println();
	}

	public void close() {
		writer.close();
	}
}


import java.math.*;
import java.util.Scanner;
public class Main {
    final static Integer MOD = 1000000009;
    public static BigInteger Com(int a,Integer n){
        if(a==0) return BigInteger.ONE;
        if(a==n) return BigInteger.ONE;
        BigInteger fenz = new BigInteger(n.toString());
        BigInteger fenm = BigInteger.ONE;
        //System.out.println(fenz + " "+fenm);
        for(int i=1;i<a; i++){
            n--;
            BigInteger next = new BigInteger(n.toString());
            //System.out.println(next);
            fenz = fenz.multiply(next);
            //System.out.println(fenz);

        }
        for(Integer i=2;i<=a;i++){
            BigInteger next = new BigInteger(i.toString());
            fenm = fenm.multiply(next);
        }
        return fenz.divide(fenm);
    }
    public static void main(String[] args){
        Scanner in = new Scanner(System.in);
        BigInteger BigMod = new BigInteger(MOD.toString());
        while(in.hasNextInt()){
            int n = in.nextInt() ;
            int w = in.nextInt();
            int b = in.nextInt();
            //System.out.println(n+" "+w+" "+b);
            //BigInteger  res = BigInteger.ZERO;
            long res = 0;
            int Bnum = b;

                if(w < 2) continue;
                long fact_w = fact(w);
                long len_w = w - 1;
                long fact_b = fact(Bnum);
                res = res + ( fact_w * len_w ) % MOD ;
                res = (res * fact_b) % MOD;
                BigInteger mul = Com(n-3,b-1+w-2);
                //System.out.println(n-2+""mul);
                mul = mul.mod(BigMod);
                //System.out.println(mul);
                String smul = mul.toString();
                res = res * Integer.parseInt(smul) % MOD;

            System.out.println(res);
        }
    }
    private static long fact(int wnum) {
        long ans=1;
        for(int i=1;i<=wnum;i++){
            ans*=i;
            ans%=MOD;
        }
        return ans;
    }
}


import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.*;

/**
 * Created by Firеfly on 12/18/2016.
 */
public class code {
    public static void main(String[] args) throws IOException {
        Woorker worker = new Woorker();
    }
}
class Woorker {
    Woorker() throws IOException {
        FastScannerqq fs = new FastScannerqq(new InputStreamReader(System.in));
        int s = fs.nextInt();
        int x1 = fs.nextInt();
        int x2 = fs.nextInt();
        int t2 = fs.nextInt();
        int t1 = fs.nextInt();
        int p = fs.nextInt();
        int d =fs.nextInt();
        int timeto = Math.abs(x1-x2)*t1;
        if (t1<=t2) System.out.println(timeto);
        else {
            int timeto1;
            if ((p>=x1 && d == -1) || (p<=x1 && d == 1)) timeto1 = Math.abs(p-x1)*t2;
            else if (p>=x1 && d==1){ timeto1 = (2*s-p-x1)*t2; d = -1; }
            else {timeto1 = (p+x1)*t2; d = 1; }
            if (x2>=x1 && d == 1)
                timeto1+=(x2-x1)*t2;
            else if (x2>=x1 && d==-1)
                timeto1+=(x1+x2)*t2;
            else if (x2<=x1 && d==-1)
                timeto1+=(x1-x2)*t2;
            else timeto1+=(2*s-x1-x2)*t2;
            System.out.println(Math.min(timeto, timeto1));

        }

    }
}

class FastScannerqq {
    BufferedReader br;
    StringTokenizer stok;

    FastScannerqq(InputStreamReader fr) {
        br = new BufferedReader(fr);
    }

    String nextToken() throws IOException {
        while (stok == null || !stok.hasMoreTokens()) {
            String s = br.readLine();
            if (s == null)
                return null;
            stok = new StringTokenizer(s);
        }
        return stok.nextToken();
    }

    int nextInt() throws IOException {
        return Integer.parseInt(nextToken());
    }

    long nextLong() throws IOException {
        return Long.parseLong(nextToken());
    }

    double nextDouble() throws IOException {
        return Double.parseDouble(nextToken());
    }

    char nextChar() throws IOException {
        return (char) br.read();
    }

    String nextLine() throws IOException {
        return br.readLine();
    }
}


import java.util.*;
import java.io.*;

public class Prob_Hanoi{
    public static long V[][];
    public static boolean Visited[][][];
    public static long MEMO[][][];
    public static void main(String args[]){
        Scanner input=new Scanner(System.in);
        V=new long[3][];
        V[0]=new long[3];
        V[1]=new long[3];
        V[2]=new long[3];
        for(int i=0;i<3;i++){
            for(int j=0;j<3;j++){
                V[i][j]=input.nextLong();
            }
        }
        int n=input.nextInt();
        Visited=new boolean[n+1][][];
        MEMO=new long[n+1][][];
        for(int i=0;i<=n;i++){
            Visited[i]=new boolean[3][];
            MEMO[i]=new long[3][];
            for(int j=0;j<3;j++){
                Visited[i][j]=new boolean[3];
                for(int k=0;k<3;k++){
                    Visited[i][j][k]=false;
                }
                MEMO[i][j]=new long[3];
            }
        }
        onlyOne();
        for(int i=1;i<n;i++){
            for(int j=0;j<3;j++){
                for(int k=0;k<3;k++){
                    if(j!=k){
                     //   System.out.println(i+""+j+""+k+")="+DP(i,j,k));
                    }
                }
            }
        }
        System.out.println(DP(n,0,2));

    }
    public static void onlyOne(){
        for(int i=0;i<3;i++){
            for(int j=0;j<3;j++){
                    if(i==j) continue;
                    int k=0+1+2-i-j;
                    MEMO[1][i][j]=Math.min(V[i][j],V[i][k]+V[k][j]);
                    Visited[1][i][j]=true;
            }
        }
    }
    public static long DP(int n,int i,int j){
        if(Visited[n][i][j])
            return MEMO[n][i][j];
        int k=0+1+2-i-j;
        MEMO[n][i][j]=Math.min(DP(n-1,i,k)+V[i][j]+DP(n-1,k,j),
                               DP(n-1,i,j)+V[i][k]+DP(n-1,j,i)+V[k][j]+DP(n-1,i,j));
       Visited[n][i][j]   =true;
       return MEMO[n][i][j];
    }
}


import java.util.*;

public class PostOffice {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		long d = 0;
		long a, b, t, k;
		Scanner sc = new Scanner(System.in);
		d = sc.nextLong();
		k = sc.nextLong();
		a = sc.nextLong();
		b = sc.nextLong();
		t = sc.nextLong();

		long answer = -1;

		if(d<k){
			answer = d*a;
		}

		else if(k*a+t >= k*b) {
			answer = k*a + (d-k)*b;
		}
		else{
			long q = d/k;
			long r = d - q*k;
			if(t+r*a<r*b){
				answer = (k*a+t)*q + r*a;
			}
			else{
				answer = (k*a+t)*q+r*b-t;
			}
		}
		System.out.println(answer);
	}

}


// practice with rainboy
import java.io.*;
import java.util.*;

public class CF946F extends PrintWriter {
	CF946F() { super(System.out, true); }
	Scanner sc = new Scanner(System.in);
	public static void main(String[] $) {
		CF946F o = new CF946F(); o.main(); o.flush();
	}

	static final int MD = 1000000007;
	long power(int a, int k) {
		if (k == 0)
			return 1;
		long p = power(a, k >> 1);
		p = p * p % MD;
		if ((k & 1) == 1)
			p = p * a % MD;
		return p;
	}
	void main() {
		int n = sc.nextInt();
		int x = sc.nextInt();
		byte[] cc = sc.next().getBytes();
		if (x == 0) {
			println(n == 1 && cc[0] == '0' ? 1 : 0);
			return;
		}
		int[] ff = new int[x + 1];
		ff[0] = ff[1] = 1;
		for (int i = 2; i <= x; i++)
			ff[i] = (ff[i - 1] + ff[i - 2]) % (MD - 1);
		for (int i = 0; i <= x; i++)
			ff[i] = (int) power(2, ff[i]);
		int[][][] ds = new int[x + 1][n][n + 1];
		for (int l = 0; l < n; l++) {
			ds[0][l][l] = ds[1][l][l] = 1;
			ds[0][l][l + 1] = cc[l] == '0' ? 1 : 0;
			ds[1][l][l + 1] = cc[l] == '1' ? 1 : 0;
		}
		for (int i = 2; i <= x; i++)
			for (int l = 0; l < n; l++)
				for (int r = l; r < n; r++) {
					long z = 0;
					for (int m = l; m <= r; m++)
						z = (z + (long) ds[i - 1][l][m] * ds[i - 2][m][r]) % MD;
					ds[i][l][r] = (int) z;
				}
		int[][] dp = new int[x + 1][n];
		int[][] dq = new int[x + 1][n];
		if (n > 1) {
			dp[0][n - 1] = ds[0][n - 1][n];
			dp[1][n - 1] = ds[1][n - 1][n];
			for (int i = 2; i <= x; i++)
				for (int l = 1; l < n; l++) {
					long z = (long) dp[i - 1][l] * ff[i - 2] % MD;
					for (int m = l; m < n; m++)
						z = (z + (long) ds[i - 1][l][m] * dp[i - 2][m]) % MD;
					dp[i][l] = (int) z;
				}
			dq[0][1] = ds[0][0][1];
			dq[1][1] = ds[1][0][1];
			for (int i = 2; i <= x; i++)
				for (int r = 1; r < n; r++) {
					long z = (long) ff[i - 1] * dq[i - 2][r] % MD;
					for (int m = r; m > 0; m--)
						z = (z + (long) dq[i - 1][m] * ds[i - 2][m][r]) % MD;
					dq[i][r] = (int) z;
				}
		}
		int[] dr = new int[x + 1];
		dr[0] = ds[0][0][n];
		dr[1] = ds[1][0][n];
		for (int i = 2; i <= x; i++) {
			long z = ((long) dr[i - 1] * ff[i - 2] + (long) ff[i - 1] * dr[i - 2]) % MD;
			for (int m = 1; m < n; m++)
				z = (z + (long) dq[i - 1][m] * dp[i - 2][m]) % MD;
			dr[i] = (int) z;
		}
		println(dr[x]);
	}
}


import java.io.*;
import java.util.Arrays;
import java.util.Locale;
import java.util.StringTokenizer;
import java.util.function.Function;

public class E {
    String fileName = "E";

    public int n, m;

    public static class Event implements Comparable<Event> {
        public static final int OPEN_TYPE = 0;
        public static final int POINT_TYPE = 1;
        public static final int CLOSE_TYPE = 2;

        public int x, type;

        public Event(int x, int type) {
            this.x = x;
            this.type = type;
        }

        @Override
        public int compareTo(Event event) {
            if (Integer.compare(x, event.x) != 0)
                return Integer.compare(x, event.x);
            else return Integer.compare(type, event.type);
        }
    }

    public static final class Node {
        public int l, r;
        public int max, maxInd;

        public Node leftNode, rightNode;

        public Node(int l, int r) {
            this.l = l;
            this.r = r;
        }
    }

    public void relax(Node root) {
        if (root.leftNode.max > root.rightNode.max) {
            root.max = root.leftNode.max;
            root.maxInd = root.leftNode.maxInd;
        } else {
            root.max = root.rightNode.max;
            root.maxInd = root.rightNode.maxInd;
        }
    }

    public Node build(int l, int r, Function<Integer, Integer> getItem) {
        Node res = new Node(l, r);
        if (l == r) {
            res.max = getItem.apply(l);
            res.maxInd = l;
        } else {
            int m = (r + l) / 2;
            res.leftNode = build(l, m, getItem);
            res.rightNode = build(m + 1, r, getItem);
            relax(res);
        }
        return res;
    }

    public void set(Node root, int ind, int val) {
        if (ind < root.l || ind > root.r)
            return;
        if (root.l == root.r) {
            root.max = val;
            return;
        }
        set(root.leftNode, ind, val);
        set(root.rightNode, ind, val);
        relax(root);
    }

    public static class QueryResult {
        public int max, maxInd;

        public QueryResult(int max, int maxInd) {
            this.max = max;
            this.maxInd = maxInd;
        }

        public QueryResult(QueryResult first, QueryResult second) {
            if (first.max > second.max) {
                this.max = first.max;
                this.maxInd = first.maxInd;
            } else {
                this.max = second.max;
                this.maxInd = second.maxInd;
            }
        }
    }

    public QueryResult query(Node root, int l, int r) {
        if (r == -1)
            return new QueryResult(0, -1);
        if (r < root.l || l > root.r)
            return new QueryResult(Integer.MIN_VALUE, -1);
        if (l <= root.l && r >= root.r) {
            return new QueryResult(root.max, root.maxInd);
        }
        return new QueryResult(query(root.leftNode, l, r), query(root.rightNode, l, r));
    }

    public int[] getLens(int[] a) {
        Node root = build(0, n, ind -> 0);
        int[] len = new int[a.length];
        for (int i = 0; i < a.length; i++) {
            int curVal = a[i];
            int prevLen = query(root, 0, curVal).max;
            len[i] = prevLen + 1;
            if (query(root, curVal, curVal).max < len[i])
                set(root, curVal, len[i]);
        }
        return len;
    }

    public void solve() throws IOException {
        n = nextInt();
        m = nextInt();
        Event[] events = new Event[2 * n + m];
        for (int i = 1; i <= m; i++) {
            events[i - 1] = new Event(i, Event.POINT_TYPE);
        }
        for (int i = 0; i < n; i++) {
            int l = nextInt();
            int r = nextInt();
            events[m + 2 * i] = new Event(l, Event.OPEN_TYPE);
            events[m + 2 * i + 1] = new Event(r, Event.CLOSE_TYPE);
        }
        Arrays.sort(events);
        int[] cnt = new int[m];
        int balance = 0;
        for (int i = 0; i < events.length; i++) {
            Event curEvent = events[i];
            if (curEvent.type == Event.OPEN_TYPE) {
                balance++;
            } else if (curEvent.type == Event.CLOSE_TYPE) {
                balance--;
            } else if (curEvent.type == Event.POINT_TYPE) {
                cnt[curEvent.x - 1] = balance;
            }
        }
        int[] lenUp = getLens(cnt);
        int[] newCnt = new int[cnt.length];
        for (int i = 0; i < cnt.length; i++) {
            newCnt[i] = cnt[cnt.length - i - 1];
        }
        int[] lenDown = getLens(newCnt);
        int[] revLenDown = new int[lenDown.length];
        for (int i = 0; i < lenDown.length; i++) {
            revLenDown[i] = lenDown[lenDown.length - i - 1];
        }
        int max = 0;
        for (int i = 0; i < lenDown.length; i++) {
            int cur = lenUp[i] + revLenDown[i] - 1;
            max = Math.max(max, cur);
        }
        out.print(max);
    }

    public void run() {
        try {
            br = new BufferedReader(new InputStreamReader(System.in));
            out = new PrintWriter(System.out);

            solve();

            out.close();
        } catch (IOException e) {
            e.printStackTrace();
            System.exit(1);
        }
    }

    BufferedReader br;
    StringTokenizer in;
    PrintWriter out;

    public String nextToken() throws IOException {
        while (in == null || !in.hasMoreTokens()) {
            in = new StringTokenizer(br.readLine());
        }
        return in.nextToken();
    }

    public int nextInt() throws IOException {
        return Integer.parseInt(nextToken());
    }

    public double nextDouble() throws IOException {
        return Double.parseDouble(nextToken());
    }

    public long nextLong() throws IOException {
        return Long.parseLong(nextToken());
    }

    public static void main(String[] args) throws IOException {
        Locale.setDefault(Locale.US);
        new E().run();
    }
}