hdu(2639)——Bone Collector II(01背包第K优决策)

本文介绍了一道01背包变种问题的解决思路,该问题要求在给定体积限制下找出第K大的价值。文章详细解释了如何通过动态规划来记录不同体积下物品组合的价值,并使用归并排序进行价值去重,最终找到目标价值。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题意:

现在给你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 
*/


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值