#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define allof(obj) (obj).begin(), (obj).end() #define range(i, l, r) for(int i=l;i>1)|y_bit)) #define bit_kth(i, k) ((i >> k)&1) #define bit_highest(i) (i?63-__builtin_clzll(i):-1) #define bit_lowest(i) (i?__builtin_ctzll(i):-1) #define sleepms(t) std::this_thread::sleep_for(std::chrono::milliseconds(t)) using ll = long long; using ld = long double; using ul = uint64_t; using pi = std::pair; using pl = std::pair; using namespace std; template std::ostream &operator << (std::ostream &dest, const std::pair &p) { dest << p.first << ' ' << p.second; return dest; } template std::ostream &operator << (std::ostream &dest, const std::tuple &t) { dest << std::get<0>(t) << ' ' << std::get<1>(t); return dest; } template std::ostream &operator << (std::ostream &dest, const std::tuple &t) { dest << std::get<0>(t) << ' ' << std::get<1>(t) << ' ' << std::get<2>(t); return dest; } template std::ostream &operator << (std::ostream &dest, const std::tuple &t) { dest << std::get<0>(t) << ' ' << std::get<1>(t) << ' ' << std::get<2>(t) << ' ' << std::get<3>(t); return dest; } template std::ostream &operator << (std::ostream &dest, const std::vector> &v) { int sz = v.size(); if (!sz) return dest; for (int i = 0; i < sz; i++) { int m = v[i].size(); for (int j = 0; j < m; j++) dest << v[i][j] << (i != sz - 1 && j == m - 1 ? '\n' : ' '); } return dest; } template std::ostream &operator << (std::ostream &dest, const std::vector &v) { int sz = v.size(); if (!sz) return dest; for (int i = 0; i < sz - 1; i++) dest << v[i] << ' '; dest << v[sz - 1]; return dest; } template std::ostream &operator << (std::ostream &dest, const std::array &v) { if (!sz) return dest; for (int i = 0; i < sz - 1; i++) dest << v[i] << ' '; dest << v[sz - 1]; return dest; } template std::ostream &operator << (std::ostream &dest, const std::set &v) { for (auto itr = v.begin(); itr != v.end();) { dest << *itr; itr++; if (itr != v.end()) dest << ' '; } return dest; } template std::ostream &operator << (std::ostream &dest, const std::map &v) { for (auto itr = v.begin(); itr != v.end(); ) { dest << '(' << itr->first << ", " << itr->second << ')'; itr++; if (itr != v.end()) dest << '\n'; } return dest; } template vector make_vec(size_t sz, T val) { return std::vector(sz, val); } template auto make_vec(size_t sz, Tail ...tail) { return std::vector(tail...))>(sz, make_vec(tail...)); } template vector read_vec(size_t sz) { std::vector v(sz); for (int i = 0; i < (int)sz; i++) std::cin >> v[i]; return v; } template auto read_vec(size_t sz, Tail ...tail) { auto v = std::vector(tail...))>(sz); for (int i = 0; i < (int)sz; i++) v[i] = read_vec(tail...); return v; } // x / y以上の最小の整数 ll ceil_div(ll x, ll y) { assert(y > 0); return (x + (x > 0 ? y - 1 : 0)) / y; } // x / y以下の最大の整数 ll floor_div(ll x, ll y) { assert(y > 0); return (x + (x > 0 ? 0 : -y + 1)) / y; } void io_init() { std::cin.tie(nullptr); std::ios::sync_with_stdio(false); } #include struct hopcroft_karp { private: int N, M; std::vector> info; std::vector> G; std::vector matchL, matchR; public: hopcroft_karp() : N(0), M(0) {} hopcroft_karp(int n, int m) : N(n), M(m), G(n), matchL(N, -1), matchR(M, -1) {} int add_edge(int s, int t) { assert(0 <= s && s < N); assert(0 <= t && t < M); int m = int(info.size()); info.push_back({s, t}); G[s].push_back(t); return m; } struct edge { int s, t; bool used; }; edge get_edge(int i) { int m = int(info.size()); assert(0 <= i && i < m); auto [s, t] = info[i]; return edge{s, t, matchL[s] == t}; } std::vector edges() { int M = int(info.size()); std::vector result; for (int i = 0; i < M; i++) result.push_back(get_edge(i)); return result; } int max_flow() { return max_flow(std::numeric_limits::max()); } int max_flow(int flow_limit) { std::vector level(N), iter(N), que(N); auto bfs = [&]() { std::fill(level.begin(), level.end(), -1); int l = 0, r = 0; for (int i = 0; i < N; i++) { if (matchL[i] == -1) { level[i] = 0; que[r++] = i; } } while (l < r) { int v = que[l++]; for (int t : G[v]) { int tt = matchR[t]; if (tt == -1 || level[tt] != -1) continue; level[tt] = level[v] + 1; que[r++] = tt; } } }; auto dfs = [&](auto self, int v) -> bool { int level_v = level[v]; for (int& i = iter[v]; i < int(G[v].size()); i++) { int t = G[v][i]; int tt = matchR[t]; if (tt == -1 || (level[tt] > level_v && self(self, tt))) { matchR[t] = v; matchL[v] = t; return true; } } return false; }; int flow = 0; while (flow < flow_limit) { bfs(); std::fill(iter.begin(), iter.end(), 0); bool ok = false; for (int i = 0; i < N && flow < flow_limit; i++) { if (matchL[i] != -1) continue; bool f = dfs(dfs, i); flow += f; ok |= f; } if (!ok) break; } return flow; } std::vector pairL() const { return matchL; } std::vector pairR() const { return matchR; } std::vector> pairs() const { std::vector> res; for (int i = 0; i < N; i++) { if (matchL[i] != -1) { res.push_back({i, matchL[i]}); } } return res; } // 点カバー (= max_flow) (max_flowを先に呼ぶ) // {0, i} : 左のi {1, i} := 右のi std::vector> min_vertex_cover() { std::vector visitedL(N, false), visitedR(M, false); auto dfs = [&](auto self, int v) { visitedL[v] = true; if (matchL[v] != -1) return; for (int t : G[v]) { visitedR[t] = true; if (matchR[t] == -1 || visitedL[matchR[t]]) continue; self(self, matchR[t]); } }; for (int i = 0; i < N; i++) { if (matchL[i] == -1 && !visitedL[i]) { dfs(dfs, i); } } std::vector> res; for (int i = 0; i < N; i++) { if (!visitedL[i]) res.push_back({0, i}); } for (int i = 0; i < M; i++) { if (visitedR[i]) res.push_back({1, i}); } return res; } // 最大独立集合 (= (N + M) - max_flow) (max_flowを先に呼ぶ) // {0, i} : 左のi {1, i} := 右のi std::vector> max_independent_set() { // } }; int main() { io_init(); int H, W; std::cin >> H >> W; auto A = read_vec(H, W); std::vector> R, C; for (int i = 0; i < H; i++) { for (int j = 0; j < W; j++) { if (A[i][j] == 0) continue; R.push_back({i, A[i][j]}); C.push_back({j, A[i][j]}); } } sort(allof(R)); R.erase(unique(allof(R)), R.end()); sort(allof(C)); C.erase(unique(allof(C)), C.end()); hopcroft_karp G(R.size(), C.size()); for (int i = 0; i < H; i++) { for (int j = 0; j < W; j++) { if (A[i][j] == 0) continue; int idx1 = std::lower_bound(allof(R), std::pair{i, A[i][j]}) - R.begin(); int idx2 = std::lower_bound(allof(C), std::pair{j, A[i][j]}) - C.begin(); G.add_edge(idx1, idx2); } } int f = G.max_flow(); assert(f == G.min_vertex_cover().size()); std::cout << f << '\n'; }