#include <algorithm>
#include <iostream>
#include <set>
#include <utility>
#include <vector>

using namespace std;

/**
 * General approach:
 *
 * Every edge (space between two cells) needs to be covered if it is not
 * between two cells of the same region. All the border edges can only be
 * covered by a single region. The other edges can be covered by either of the
 * adjacent regions. In graph language, we have a graph where nodes are regions
 * and edges connect adjacent regions. Because the graph of regions is
 * bipartite, we can solve this min vertex cover by instead finding a maximum
 * matching in the graph.
 *
 * Note that this is not an efficient solution: There's heavy use of sets, the
 * graph is not made to be small, and it uses a DFS-based matching algorithm
 * instead of a faster flow algorithm.
 */
struct DSU {
    int n;
    // uf[x] = parent of x or negative size of component
    vector<int> uf;
    DSU(int n): n(n) {
        uf.assign(n, -1);
    }

    int find(int x) {
        return uf[x] < 0 ? x : (uf[x] = find(uf[x]));
    }

    int merge(int x, int y) {
        int xr = find(x);
        int yr = find(y);
        if (xr == yr)
            return 0;

        if (uf[yr] < uf[xr]) {
            uf[yr] += uf[xr];
            uf[xr] = yr;
        } else {
            uf[xr] += uf[yr];
            uf[yr] = xr;
        }

        return 1;
    }

    int get_size(int x) {
        return -uf[find(x)];
    }

    inline bool same(int x, int y) {
        return find(x) == find(y);
    }
};

bool find(const vector<vector<int>>& graph, vector<int>& match, vector<bool>& seen, int j) {
    if (match[j] == -1) return true;

    seen[j] = true;
    int di = match[j];
    for (auto& e : graph[di]) {
        if (!seen[e] and find(graph, match, seen, e)) {
            match[e] = di;
            return true;
        }
    }

    return false;
}

int dfs_matching(const vector<vector<int>>& graph, int n, int m) {
    vector<int> match;
    vector<bool> seen;
    match.assign(m, -1);
    for (int i = 0; i < n; ++i) {
        seen.assign(m, 0);
        for (auto& j : graph[i]) {
            if (find(graph, match, seen, j)) {
                match[j] = i;
                break;
            }
        }
    }

    return m - count(begin(match), end(match), -1);
}

int main() {
    int n, m;
    cin >> n >> m;
    vector<string> grid(n);

    for (int i = 0; i < n; ++i) {
        cin >> grid[i];
    }

    DSU dsu(n * m);

    auto node_id = [&m](int i, int j) {
        return m * i + j;
    };

    for (int i = 0; i < n; ++i) {
        for (int j = 0; j < m; ++j) {
            if (i + 1 < n && grid[i][j] == grid[i + 1][j]) {
                dsu.merge(node_id(i, j), node_id(i + 1, j));
            }

            if (j + 1 < m && grid[i][j] == grid[i][j + 1]) {
                dsu.merge(node_id(i, j), node_id(i, j + 1));
            }
        }
    }

    set<int> border_regions;
    set<pair<int, int>> edges;
    vector<vector<int>> match_graph(n * m);
    for (int i = 0; i < n; ++i) {
        border_regions.insert(dsu.find(node_id(i, 0)));
        border_regions.insert(dsu.find(node_id(i, m - 1)));
    }

    for (int j = 0; j < m; ++j) {
        border_regions.insert(dsu.find(node_id(0, j)));
        border_regions.insert(dsu.find(node_id(n - 1, j)));
    }

    for (int i = 0; i < n; ++i) {
        for (int j = 0; j < m; ++j) {
            if (i + 1 < n && grid[i][j] != grid[i + 1][j]) {
                int u = dsu.find(node_id(i, j));
                int v = dsu.find(node_id(i + 1, j));

                if (grid[i][j] == '1') {
                    swap(u, v);
                }

                pair<int, int> p = make_pair(u, v);
                if (!border_regions.count(u) && !border_regions.count(v) && !edges.count(p)) {
                    edges.insert(p);
                    match_graph[u].push_back(v);
                }
            }

            if (j + 1 < m && grid[i][j] != grid[i][j + 1]) {
                int u = dsu.find(node_id(i, j));
                int v = dsu.find(node_id(i, j + 1));

                if (grid[i][j] == '1') {
                    swap(u, v);
                }

                pair<int, int> p = make_pair(u, v);
                if (!border_regions.count(u) && !border_regions.count(v) && !edges.count(p)) {
                    edges.insert(p);
                    match_graph[u].push_back(v);
                }
            }
        }
    }

    cout << (border_regions.size() + dfs_matching(match_graph, n * m, n * m)) << '\n';

    return 0;
}
