2023.04.14 定时测试随笔 T1

T1 P2170 选学霸

传送门:洛谷P2170

本题考察的是并查集优化背包DP,所以我们通过并查集将 \(n\) 个点变成 \(group\) 个连通块,那么每个连通块里面的点要么都选要么都不选,状态 \(dp[i]\) 定义为可以选 \(i\) 个学霸且不会抗议,算出所有可能的结果,再枚举 \(1\) ~ \(n\) ,求出最接近 \(m\) 的可能的值;


贴代码:

#include <bits/stdc++.h>

using namespace std;

const int maxn = 2e4 + 5;
int n, m, k, group, dp[maxn], fa[maxn], siz[maxn], f[maxn];

int Find(int x) {
    if (x == fa[x]) return fa[x];
    return fa[x] = Find(fa[x]);//很重要的路径压缩
}

void read() {
    scanf("%d%d%d", &n, &m, &k);
    group = n;
    for (int i = 1; i <= n; ++ i)
        fa[i] = i, siz[i] = 1;
    for (int i = 1, u, v; i <= k; ++ i) {
        scanf("%d%d", &u, &v);
        int fu = Find(u), fv = Find(v);
        if (fu != fv) {
            group --;
            fa[fu] = fv;
            siz[fv] += siz[fu];
        }
    }
    dp[0] = dp[n] = 1;
    for (int i = 1; i <= n; ++ i) {
        int fi = Find(i);
        if (f[fi]) continue ;
        f[fi] = 1;
        for (int j = n - siz[fi]; j >= 0; -- j) {
            if (dp[j]) dp[j + siz[fi]] = 1;
        }
    }
    int minl = 1e9 + 7, minr = 1e9 + 7;
    for (int i = 0; i <= m; ++ i)
        if (dp[i]) minl = min(minl, m - i);
    for (int i = m; i <= n; ++ i)
        if (dp[i]) minr = min(minr, i - m);
    if (minl == minr) printf("%d\n", m - minl);
    else if (minr < minl) printf("%d\n", m + minr);
    else printf("%d\n", m - minl);
}

int main() {
    read();
    return 0;
}
posted @ 2023-04-14 18:46  florence25  阅读(29)  评论(0)    收藏  举报