题意:
现在给你n个数,以及最大的体积容量V,还有我们需要求的第K大的数。并且告诉你每个骨头的价值和体积。然后叫你求出第K大的总价值。(并且,这些价值中不能产生重复,也就是要去重)
思路:
这是一道01背包的变种,只不过这里变成了查询第K优的决策。
首先我们定义dp[i][j]为当体积为i时,第j大的最大价值是多少。
对于每个物品每种体积,我们都需要把它们的前K大都存下来。于是:
我们设一个数组a[j],记录的是当体积为j时,取第i件物品的最大价值是多少。a[j]=dp[j-vol[i]][kk]+val[i];
我们设一个数组b[i],记录的是当体积为j时,不取进第i件物品的最大价值是多少。b[j]=dp[j][kk];
最重要的一点就是:我们应该在for完K之后,然后把a[kk]=-1,b[kk]=-1; 它们应该都被赋值为-1。因为这个在后面进行把a数组与b数组进行合并的时候是有用的,因为当a数组结束的时候b数组不一定是结束了。所以只有当两个数组同时为-1才能代表它们同时是结束了。
然后就是进行把a,b两个数组进行合并的操作了,这里用到了排序中的归并排序。真是很巧妙啊,这个想法!!
哦,这里我一开始在纠结的是如果要进行归并排序的话,那么a与b都应该是有序的才行,但是上面是怎么保证是有序的呢?
这里我后来找到了答案,因为我们在forkk的时候,就已经是从等级高的推向等级低的,而且,a[j]=dp[j-vol[i]][kk]+val[i],都是从第kk大的推过来的,所以这里就已经是默认了a数组是有序的,同理,b数组也是一样的。
然后我们就在归并排序中进行了去重的操作,并同时把两个数组中的较大值赋值给dp[j][cc]。
最后输出dp[V][K]就是答案!
#include<stdio.h>
#include<string.h>
#include<iostream>
#include<algorithm>
using namespace std;
#define maxn 110
int vol[maxn],val[maxn];
int dp[1100][33];
int a[maxn],b[maxn];
int main(){
int T;
scanf("%d",&T);
while(T--){
int n,V,K;
memset(dp,0,sizeof(dp));
scanf("%d%d%d",&n,&V,&K);
int aa,bb,cc;
for(int i=1;i<=n;i++) scanf("%d",&val[i]);
for(int i=1;i<=n;i++) scanf("%d",&vol[i]);
for(int i=1;i<=n;i++){
for(int j=V;j>=vol[i];j--){
int kk=1;
for(kk=1;kk<=K;kk++){
a[kk]=dp[j-vol[i]][kk]+val[i];
b[kk]=dp[j][kk];
}
int aa,bb,cc;
aa=bb=cc=1;
a[kk]=b[kk]=-1;
while(cc<=K&&(a[aa]!=-1||b[bb]!=-1)){
if(a[aa]>b[bb]){
dp[j][cc]=a[aa++];
}
else{
dp[j][cc]=b[bb++];
}
if(dp[j][cc-1]!=dp[j][cc]){ //这里是进行去重的操作,因为只有当前后两个不相等时cc才会往后加
cc++;
}
}
}
}
printf("%d\n",dp[V][K]);
}
}
/*
1
5 10 2
1 2 3 4 5
5 4 3 2 1
1
5 10 4
1 2 3 4 5
5 4 3 2 1
5 10 16
1 2 3 4 5
5 4 3 2 1
*/