JOI君有N个装在手机上的挂饰,编号为1...N。 JOI君可以将其中的一些装在手机上。
JOI君的挂饰有一些与众不同——其中的一些挂饰附有可以挂其他挂件的挂钩。每个挂件要么直接挂在手机上,要么挂在其他挂件的挂钩上。直接挂在手机上的挂件最多有1个。
此外,每个挂件有一个安装时会获得的喜悦值,用一个整数来表示。如果JOI君很讨厌某个挂饰,那么这个挂饰的喜悦值就是一个负数。
JOI君想要最大化所有挂饰的喜悦值之和。注意不必要将所有的挂钩都挂上挂饰,而且一个都不挂也是可以的。
第一行一个整数N,代表挂饰的个数。
接下来N行,第i行(1<=i<=N)有两个空格分隔的整数Ai和Bi,表示挂饰i有Ai个挂钩,安装后会获得Bi的喜悦值。
输出一行一个整数,表示手机上连接的挂饰总和的最大值
Solution
与普通背包不同的地方在于,这里背包的容量是动态的,所以用f[i][j]表示前i件物品中,满足还剩j个挂钩空着的最大愉悦值,就解决了背包容量未知的问题,然后状态转移方程的推导与01背包相似,如果不取第i个,最优解就是f[i-1][j],如果不取,f[i-1][max(j-a[i],0)+1]+b[i]。因为如果不取这个这个挂饰,挂钩数就只剩下j-a[i]+1个,但这个表达式的值有可能是一个负数,没有意义,所以要取最差的情况,即舍弃所有挂饰,只剩手机上原来的那个挂钩,也就是1个挂钩。
但是有些时候,j-a[i]+1为负数也可能有意义。因为挂钩的位置没有要求,只要后来的挂钩能把j补成非负数就是合法的,但如果去考虑这个,动规的循环变量就不确定,所以要把挂饰按能提供的挂钩数量从大到小排序,就能避免这种情况。
(参考:http://www.cnblogs.com/2014nhc/p/6231288.html)
Code
1 #include<iostream>
2 #include<cstdio>
3 #include<cstring>
4 #include<algorithm>
5 using namespace std;
6 int f[2111][2111];
7
8 struct rec
9 {
10 int a,b;
11 }x[2111];
12
13 int cmp(const rec&x1,const rec&x2)
14 {
15 return x1.a>x2.a;
16 }
17
18 int max(int a,int b)
19 {
20 return a>b?a:b;
21 }
22
23 int main()
24 {
25 int n;
26 scanf("%d",&n);
27 for (int i=1; i<=n; i++)
28 scanf("%d%d",&x[i].a,&x[i].b);
29 sort(x+1,x+n+1,cmp);
30 memset(f,-210000000,sizeof(f));
31 f[0][1]=0;
32 for (int i=1; i<=n; i++)
33 for (int j=0; j<=n; j++)
34 f[i][j]=max(f[i-1][j],f[i-1][max(j-x[i].a,0)+1]+x[i].b);
35 int ans=0;
36 for (int i=0; i<=n; i++)
37 ans=max(ans,f[n][i]);
38 printf("%d",ans);
39 return 0;
40 }
Source
http://www.lydsy.com/JudgeOnline/problem.php?id=4247