DP题 总结 [更新中]
建设中 ...
预防针 : 本蒟蒻代码风格清奇(⊙﹏⊙)b
一.选学霸
题目描述
老师想从N名学生中选M人当学霸,但有K对人实力相当,如果实力相当的人中,一部分被选上,另一部分没有,同学们就会抗议。所以老师想请你帮他求出他该选多少学霸,才能既不让同学们抗议,又与原来的M尽可能接近
输入输出格式
输入格式:
第一行,三个正整数N,M,K。
第2...K行,每行2个数,表示一对实力相当的人的编号(编号为1…N)
输出格式:
一行,表示既不让同学们抗议,又与原来的M尽可能接近的选出学霸的数目。(如果有两种方案与M的差的绝对值相等,选较小的一种:)
输入输出样例
输入样例#1:
4 3 2 1 2 3 4
输出样例#1:
2
说明
100%的数据N,P<=20000
分析:
看到这题第一下想出的就是并查集, 但仔细想想很容易想到背包问题。
因为所有水平一样的人必须一起选, 所以, 把水平一样的人看成一件物品。再01背包转移。
注意此时的最大体积应该为2*m(很容易想到)。最后再枚举体积。最后再注意一下差值一样时取最小。
AC水题;
//By zZhBr #include <iostream> #include <cstdio> #include <cstring> #include <cmath> using namespace std; int n, m, k; int f[20010]; inline int find(int x) { return x == f[x] ? f[x] : f[x] = find(f[x]); } int a[20010]; int dp[20010]; int num[20010]; int vis[20010]; int cnt; int main() { scanf("%d%d%d", &n, &m, &k); for(register int i = 1 ; i <= n ; i ++) f[i] = i; for(register int i = 1 ; i <= k ; i ++) { int x, y; scanf("%d%d", &x, &y); int fx = find(x), fy = find(y); f[fx] = fy; } for(register int i = 1 ; i <= n ; i ++) { int fa = find(i); num[fa]++; if(!vis[fa]) { vis[fa] = 1; a[++cnt] = fa; } } for(register int i = 1 ; i <= cnt ; i ++) { for(register int j = m * 2 ; j >= num[a[i]] ; j --) { dp[j] = max(dp[j], dp[j-num[a[i]]] + num[a[i]]); } } int ans = 0; for(register int i = 0 ; i <= m * 2 ; i ++) { if(abs(ans - m) > abs(dp[i] - m)) ans = dp[i]; else if(abs(ans - m) == abs(dp[i] - m)) ans = min(ans, dp[i]); } printf("%d", ans); return 0; }