背包第K优解

背包第K优解问题

(这个东西其实早就应该会的,鬼知道怎么就拖到现在。一位学弟说自己写了一道背包k有解的题,想了想突然发现自己不太会,我果然好菜啊……)

  • 写这篇博客的目的不是说A了这道题来讲思路,那样没什么用。毕竟这是提高甚至普及知识点,没什么可吹嘘的。写此文的目的在于总结第k优解这类问题,下面我们来深入讨论一下。
  • 当然,我们仍然以01背包为例。我们在求01背包最优解的时候,有这样一个式子, f [ i ] [ j ] = m a x ( f [ i − 1 ] [ j ] , f [ i − 1 ] [ j − v [ i ] ] + w [ i ] ) f[i][j]=max(f[i-1][j],f[i-1][j-v[i]]+w[i]) f[i][j]=max(f[i1][j],f[i1][jv[i]]+w[i])。他们两个里面一个会是当前状态 f [ i ] [ j ] f[i][j] f[i][j]的最优解和次优解。因为目前对这个状态有用的决策只有两个,那么肯定一个是最优解一个是次优解。
  • 推广到第k优解呢?我们把状态拓展一维, f [ i ] [ j ] [ k ] f[i][j][k] f[i][j][k]表示到第i个物品,使用j的空间的第k优解。对于我们来说,有用的状态还是 f [ i − 1 ] [ j ] f[i-1][j] f[i1][j] f [ i − 1 ] [ j − v [ i ] ] f[i-1][j-v[i]] f[i1][jv[i]],只不过他们此时不再是一个单值了,而是一个有序的序列,对,你把它当成一个数组。我们要求当前状态的前K优解,其实就是从这两个序列里选出k个最优的。那么明显可以用一个双指针,比较加进去就好了。
  • 细节问题在于初始化。最开始我以为反正不会出现负数的情况,直接默认为0,不管不就好了?可是仔细考虑一下f数组的定义,当i=0的时候,只有 f [ 0 ] [ 0 ] [ 1 ] f[0][0][1] f[0][0][1]是合法的状态,其他的都不合法。所以我们需要把f数组初始化为负无穷,使 f [ 0 ] [ 0 ] [ 1 ] = 0 f[0][0][1]=0 f[0][0][1]=0 n ∗ k ∗ v n*k*v nkv递推即可。
#include<bits/stdc++.h>
using namespace std;
int n,m,k,f[5010][51],v[210],w[210],c[51];
int main(){
	scanf("%d%d%d",&k,&m,&n);
	for(int i=1;i<=n;++i) scanf("%d%d",&v[i],&w[i]);
	memset(f,0xcf,sizeof(f));
	f[0][1]=0;
	for(int i=1;i<=n;++i){
		for(int j=m;j>=v[i];--j){
			int l=1,r=1,t=0,tmp=0;
			while(t<k){
				if(f[j][l]<f[j-v[i]][r]+w[i])
				c[++tmp]=f[j-v[i]][r]+w[i],r++;
				else c[++tmp]=f[j][l],l++;
				t++;
			}
			for(int o=1;o<=k;++o) f[j][o]=c[o];
		}
	}int ans=0;
	for(int i=1;i<=k;++i) ans+=f[m][i];
	cout<<ans<<endl;
	return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值