「JOISC 2014 Day4」挂饰(背包DP)题解

题目翻译

JOI 君有 n n 个装在手机上的挂饰,编号为 1 n 1 \ldots n 。 JOI 君可以将其中一些挂饰装在手机上。
JOI 君的挂饰有一些与众不同——其中的一些挂饰附有挂钩,你可以将其他挂饰挂在挂钩上。 i i 号挂饰有 A i A_i 个挂钩。每个挂件要么直接挂在手机上,要么挂在其他挂件的挂钩上。直接挂在手机上的挂件最多有 1 1 个。
此外,每个挂件有一个安装时会获得的喜悦值,用一个整数来表示。如果 JOI 君很讨厌某个挂饰,那么这个挂饰的喜悦值就是一个负数。
JOI 君想要最大化所有挂饰的喜悦值之和。注意不必要将所有的挂钩都挂上挂饰,而且一个都不挂也是可以的。

输入格式

第一行一个整数 n n ,代表挂饰的个数。
接下来 n n 行,第 i i 行有两个空格分隔的整数 A i A_i B i B_i ,表示挂饰 i i A i A_i 个挂钩,安装后会获得 B i B_i 的喜悦值。

输出格式

输出一行,一个整数,表示手机上连接的挂饰总和的最大值。

样例

样例输入

5
0 4
2 -2
1 -1
0 1
0 3

样例输出

5

样例说明

挂饰 2 2 直接挂在手机上,然后将挂饰 1 1 和挂饰 5 5 分别挂在挂饰 2 2 的两个挂钩上,可以获得最大喜悦值 4 2 + 3 = 5 4-2+3=5

分析

首先,这显然是一道01背包问题。定义 d p [ i ] dp[i] 为剩余挂饰 i i 个能获得的最大价值。容易写出 O ( n m ) O(nm) 的01板子,可这样会TLE。
不妨设想一下,对于第 i i 个挂饰,如果之前有超过 n n 个剩余挂钩,那么就算不用这个挂饰的挂钩,也不影响最后答案。
这样的话,第二重循环便只用循环到 n n ,时间复杂度 O ( n 2 ) O(n^2)
这里还需要注意两个细节:
1.若 α \alpha β \beta 喜悦值大,比 β \beta 挂钩少,若选 β \beta 是正解,如果 α \alpha β \beta 前面,我们选的会是 α \alpha ,如果 β \beta α \alpha 前面,则不影响答案。
所以这里要用贪心的思想,将挂钩多的挂饰放在前面处理。
2.第二重循环为什么一个正序一个逆序,这个希望读者自行思考。(提示:一维数组01背包的性质)

代码

#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <climits>
using namespace std;
const int MAXN = 2005, inn = INT_MIN;
struct Node {
	int A, B;
}arr[MAXN * MAXN];
int dp[MAXN];
bool vis[MAXN];
bool cmp(Node x, Node y) {
	return x.A > y.A;
}
int main() {
	int n, maxx = 0;
	scanf("%d", &n);
	for(int i = 1; i <= n; i ++) {
		scanf("%d%d", &arr[i].A, &arr[i].B);
	}
	sort(arr + 1, arr + 1 + n, cmp);//贪心 
	for(int i = 1; i <= n; i ++) dp[i] = inn;
	dp[1] = 0; vis[1] = 1;
	for(int i = 1; i <= n; i ++) {//01背包 
		if(arr[i].A > 0) {
			for(int j = n; j >= 1; j --) { 
				if(!vis[j]) continue;
				vis[min(j - 1 + arr[i].A, n)] |= vis[j];
				dp[min(j - 1 + arr[i].A, n)] = max(dp[min(j - 1 + arr[i].A, n)], dp[j] + arr[i].B);
			}
		}
		else {
			for(int j = 1; j <= n; j ++) {
				if(!vis[j]) continue;
				vis[min(j - 1 + arr[i].A, n)] |= vis[j];
				dp[min(j - 1 + arr[i].A, n)] = max(dp[min(j - 1 + arr[i].A, n)], dp[j] + arr[i].B);
			}
		}
	}
	for(int i = 0; i <= n; i ++) {
		maxx = max(maxx, dp[i]);
	}
	printf("%d", maxx);
	return 0;
}

猜你喜欢

转载自blog.csdn.net/Clever_Hard/article/details/106786321