P2170 选学霸(并查集+01背包)

做题一定得有清晰的思路!!!我真该死啊

实力的相似就归纳为一个集合,对于组建集合可以联想到并查集。

当我们把集合全部分配好了之后,就需要面对所有集合的选和不选两个决策,因为题意告诉我们一个集合内的人,要么全选,要么不选。然后题目又给出M,让我们选集合并输出最接近M的总人数(包括M)。那这不就是一个背包问题了嘛?(选与不选——装和不装,接近M——背包容量)。但是这个背包是可以超过M的,所以我们在倒着装的时候容量开2*M就好啦。

代码附上:

#include<math.h>
#include<algorithm>
#include<time.h>
#include<stdlib.h>
#include<iostream>
#include<string.h>
#include<sstream>
#include<string>
#include<map>
#include<list>
#include<queue>
#include<set>
#include<vector>
#include<stack>
#include<limits>
#define re register
#define iosgo() std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
#define run(i,n) for (int i = 1; i <= n; i++)
#define cin std::cin
#define cout std::cout
#define ll long long
#define endl "\n"
using namespace std;
typedef pair<int, int>pll;
const int N = 2e6 + 10;
pll c[N];
int x[N], y[N], dp[N], ss[N], a[N], b[N];
bool flag[N];
int gcd(int a, int b)
{
    return b ? gcd(b, a % b) : a;
}
int find(int v)
{
    if (x[v] == v)return v;
    else return x[v] = find(x[v]);
}
void merge(int v, int u)
{
    int o = find(v);
    int q = find(u);
    if (o != q)
    {
        x[o] = q;
    }
}
signed main()
{
    int n, m, k;
    cin >> n >> m >> k;
    run(i, n)x[i] = i;
    run(i, k)
    {
        int s, d;
        cin >> s >> d;
        merge(s, d);    
    }
    run(i, n)
    {
        x[i] = find(x[i]);
        y[x[i]]++;
    }
    int cnt = 0;
    run(i, n)
    {
        if (y[i])
        {
            a[++cnt] = y[i];//把集合封装,转化为物品重量
        }
    }
    run(i, cnt)
    {
        for (int j = m + m; j >= a[i]; j--)//倒着开2*M
        {
            dp[j] = max(dp[j], dp[j - a[i]] + a[i]);//装装装
        }
    }
    int ans = INT_MAX;
    run(i, n)
    {
        if (abs(dp[i] - m) < abs(ans - m))ans = dp[i];//取最接近的
    }
    cout << ans;
}
    
    

猜你喜欢

转载自blog.csdn.net/YZcheng_plus/article/details/129467303