题目描述
老师想从 n 名学生中选 m 人当学霸,但有 k 对人实力相当,如果实力相当的人中,一部分被选上,另一部分没有,同学们就会抗议。所以老师想请你帮他求出他该选多少学霸,才能既不让同学们抗议,又与原来的 m 尽可能接近。
输入格式
第一行,三个正整数 n,m,k。
接下来 k 行,每行 2 个数,表示一对实力相当的人的编号(编号为 1,2,⋯n)。
输出格式
共一行,表示既不让同学们抗议,又与原来的 m 尽可能接近的选出学霸的数目。
如果有两种方案与 m 的差的绝对值相等,选较小的一种。
思路:背包dp,并查集
实力相当的处理
如果有 x 个人实力相当,可以考虑将这 x 个人放到并查集中维护。
每新增一个实力相当的人,就放到对应的并查集中。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| int root[N];
int get(int x) { if(x != root[x]) { root[x] = get(root[x]); return root[x]; } else return root[x]; }
void merge(int x, int y) { root[get(y)] = get(x); }
void init() { for(int i = 1; i <= n; i++) { root[i] = i; } }
|
学霸数目
经过上一步之后,成绩相当的人都在并查集中。
考虑每一个集合中的元素个数为花费,那么就可以转化为背包dp。
之后,就是常规的0/1背包。
1 2 3 4 5
| for(int i = 1; i < pt; i++) { for(int j = 2 * m; j >= cnt[i]; j--) { dp[j] |= dp[j - cnt[i]]; } }
|
最近数量
从 m 向外出发,最近的一个数就是答案。
1 2 3 4 5 6 7 8 9 10
| for(int i = 0;; i++) { if(dp[m - i]) { cout << m - i << endl; return 0; } if(dp[m + i]) { cout << m + i << endl; return 0; } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75
| #include <bits/stdc++.h> #define set(x, y) memset(x, y, sizeof x)
using namespace std; using ll = long long;
const int inf = 0x7f7f7f7f;
const int N = 2e4 + 10;
int n, m, k; int root[N]; int head[N]; int cnt[N]; bool dp[2 * N]; int pt = 1;
int get(int x) { if(x != root[x]) { root[x] = get(root[x]); return root[x]; } else return root[x]; }
void merge(int x, int y) { root[get(y)] = get(x); }
void init() { for(int i = 1; i <= n; i++) { root[i] = i; } }
int main() { #ifndef ONLINE_JUDGE freopen("diyi.in", "r", stdin); freopen("diyi.out", "w", stdout); #endif cin >> n >> m >> k; int t1, t2; init(); for(int i = 0; i < k; i++) { cin >> t1 >> t2; if(get(t1) != get(t2)) merge(t1, t2); } for(int i = 1; i <= n; i++) { int r = get(i); if(!head[r]) { head[r] = pt; pt++; } cnt[head[r]]++; } dp[0] = true; for(int i = 1; i < pt; i++) { for(int j = 2 * m; j >= cnt[i]; j--) { dp[j] |= dp[j - cnt[i]]; } } for(int i = 0;; i++) { if(dp[m - i]) { cout << m - i << endl; return 0; } if(dp[m + i]) { cout << m + i << endl; return 0; } } return 0; }
|