题目:
OI君有N个装在手机上的挂饰,编号为1…N。 JOI君可以将其中的一些装在手机上。
JOI君的挂饰有一些与众不同——其中的一些挂饰附有可以挂其他挂件的挂钩。每个挂件要么直接挂在手机上,要么挂在其他挂件的挂钩上。直接挂在手机上的挂件最多有1个。
此外,每个挂件有一个安装时会获得的喜悦值,用一个整数来表示。如果JOI君很讨厌某个挂饰,那么这个挂饰的喜悦值就是一个负数。
JOI君想要最大化所有挂饰的喜悦值之和。注意不必要将所有的挂钩都挂上挂饰,而且一个都不挂也是可以的。
分析:
1.状态:
dp[i][j]表示已经挂好了前i个挂饰还有j个挂钩
2.状态转移方程:
dp[i][j] = max(不放第i个, 放了第i个再加上当前第i个的魅力值)
3.具体实现:
首先要排个序,将挂钩多的挂饰先挂(尽可能加大可以挂的空间),然后想到这,可能有的人会直接一个三重暴力枚举,但是显然这是不行的,我们需要更少的时间。但是如果把程序优化到二维,那么此时就要考虑如何求得当前的挂钩数了,方法很多,这里提几个:
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
int n, dp[2005][4010], _max = -1000000005;
struct jj {
int a, b;
} c[2005];
int cmp(jj x, jj y) { return x.a > y.a; }
int main() {
memset(dp, -0x7f7f7f7f, sizeof(dp));
scanf("%d", &n);
for (int i = 1; i <= n; i++) {
scanf("%d %d", &c[i].a, &c[i].b);
}
for (int i = 0; i <= n; i++) {
for (int j = 0; j <= 2 * n; j++) {
dp[i][j] = -2100000000;
}
}
dp[0][1] = 0;
sort(c + 1, c + 1 + n, cmp);
for (int i = 1; i <= n; i++) {
for (int j = 0; j <= 2 * n; j++) {//应为若考虑极端那么最多就是两个挂钩数量为n的挂在一起所以最多2*n个钩子
if (j > 0) {
dp[i][j - 1 + c[i].a] = max(dp[i - 1][j] + c[i].b, dp[i - 1][j - 1 + c[i].a]);
//将每个dp[i][j]取第i个的情况提前计算出来
dp[i][j] = max(dp[i - 1][j], dp[i][j]);
} else {
dp[i][j] = max(dp[i - 1][j], dp[i][j]);
}
}
}
for (int i = 0; i <= 2 * n; i++) {
if (dp[n][i] > _max) {
_max = dp[n][i];
}
}
printf("%d", _max);
return 0;
}
/*5
0 4
2 -2
1 -1
0 1
0 3*/