AtCoder ABC320 G 二分 + 二分图匹配

题意

传送门 AtCoder ABC320 G Slot Strategy 2 (Hard)

题解

答案满足单调性,二分求解。将问题转化为字符串与时间点的匹配问题,单个字符串代表的节点至多向 n n n 个时间点连边即可,因为剩余 n − 1 n - 1 n1 个字符串至多匹配 n − 1 n - 1 n1 个时间点。每 m m m 段时间至少能匹配一个字符串,故二分上界为 n ⋅ m n \cdot m nm。字符串代表的二分图的一侧节点个数为 n n n,边集上界为 n 2 n^2 n2,仅从字符串节点一侧寻找增广路,匈牙利算法可以做到 O ( n 3 ) O(n^3) O(n3)。总时间复杂度 O ( d n m + d n 3 log ⁡ ( n ⋅ m ) ) O\Big(dnm + dn^3\log (n\cdot m)\Big) O(dnm+dn3log(nm)),其中 d = 10 d = 10 d=10 为字符集的规模。

#include 
using namespace std;

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    int n, m;
    cin >> n >> m;
    vector<string> ss(n);
    for (int i = 0; i < n; ++i) {
        cin >> ss[i];
    }
    vector<vector<vector<int>>> pos(10, vector<vector<int>>(n));
    for (int dig = 0; dig < 10; ++dig) {
        for (int i = 0; i < n; ++i) {
            auto &s = ss[i];
            for (int j = 0; j < m; ++j) {
                if (s[j] - '0' == dig) {
                    pos[dig][i].push_back(j);
                }
            }
        }
    }

    auto judge = [&](int dig, int tim) -> bool {
        vector<vector<int>> to(n);
        set<int> st;
        for (int i = 0; i < n; ++i) {
            auto &a = pos[dig][i];
            int sz = a.size();
            if (sz == 0) {
                return false;
            }
            for (int j = 0; j < n; ++j) {
                int k = a[j % sz] + (j / sz) * m;
                if (k <= tim) {
                    to[i].push_back(k);
                    st.insert(k);
                }
            }
        }
        vector<int> tmp(st.begin(), st.end());
        int m = tmp.size();
        vector<vector<int>> g(n + m);
        for (int i = 0; i < n; ++i) {
            for (int t : to[i]) {
                int j = lower_bound(tmp.begin(), tmp.end(), t) - tmp.begin();
                g[i].push_back(n + j);
            }
        }
        vector<int> match(n + m, -1);
        vector<bool> used(n + m);
        function<bool(int)> dfs = [&](int v) -> bool {
            used[v] = 1;
            for (int u : g[v]) {
                int w = match[u];
                if(w == -1 || (!used[w] && dfs(w))) {
                    match[u] = v;
                    match[v] = u;
                    return true;
                }
            }
            return false;
        };
        int res = 0;
        for (int v = 0; v < n; ++v) {
            fill(used.begin(), used.end(), false);
            if (match[v] == -1) {
                res += dfs(v);
            }
        }
        return res == n;
    };

    int res = -1;
    for (int dig = 0; dig < 10; ++dig) {
        int lb = -1, ub = n * m;
        while (ub - lb > 1) {
            int mid = (lb + ub) / 2;
            if (judge(dig, mid)) {
                ub = mid;
            } else {
                lb = mid;
            }
        }
        if (ub < n * m) {
            if (res == -1 || res > ub) {
                res = ub;
            }
        }
    }
    cout << res << '\n';

    return 0;
}

你可能感兴趣的:(基本算法,图论,算法)