#include #include #include #include #include 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 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>& graph, vector& match, vector& 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>& graph, int n, int m) { vector match; vector 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 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 border_regions; set> edges; vector> 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 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 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; }