做题一定得有清晰的思路!!!我真该死啊
实力的相似就归纳为一个集合,对于组建集合可以联想到并查集。
当我们把集合全部分配好了之后,就需要面对所有集合的选和不选两个决策,因为题意告诉我们一个集合内的人,要么全选,要么不选。然后题目又给出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;
}