思路:求第K大的最优值,最普通的01背包我们都只保存子问题唯一的最优值,现在则需要保存子问题的前K大的最优值,
假设在普通的01背包中,dp[i]即表示体积 i 的最优值(二维dp可优化成一维dp,详见背包九讲),dp[i][k]表示体积为 i 时的第K个最优值,那么一开始dp[i][1...k]均应初始化为0
01背包中有状态转移方程:dp[i]=max(dp[i],dp[i-v]+v),事实上就是从一个最优值衍生多一个"最优值"后,再在二者中选择最优的保留,其实此时可看做本题的k为1的情况.
所以求第K大,只需要每次保留前K个最优值,再衍生出K个“最优值”,从这2K个“最优值”中选出前K个不重复的最优值,反复如此即可。
可能表述不清,还望指出.....
AC代码(含注释):
#include<iostream>
#include<string.h>
using namespace std;
typedef long long LL;
#define min(a,b) (a>b?b:a)
#define max(a,b) (a>b?a:b)
#define init(a,num) memset(a,num,sizeof(a))
int a[1005],b[1005];
int dp[1005][35];
int main()
{
int t;
cin >> t;
while (t--)
{
int n, v, k;
cin >> n >> v >> k;
for (int i = 0; i < n; ++i)
{
cin >> b[i];
}
for (int i = 0; i < n; ++i)
{
cin >> a[i];
}
int d1[35],d2[35];
init(dp,0);
init(d1,0);
init(d2,0);
for (int j = 0; j < n; ++j)
{
for (int i = v; i >= a[j]; --i)
{
int l;
for(l=1;l<=k;++l)
{
d1[l]=dp[i][l];//上个子问题中保留的前K个最优值
d2[l]=dp[i-a[j]][l]+b[j];//由K个最优衍生出来的K个值
}
d1[l]=d2[l]=-1;
int pos,pos1,pos2;
pos=pos1=pos2=1;
while(pos<=k&&(d1[pos1]!=-1||d2[pos2]!=-1))//合二为一
{
if(d1[pos1]>d2[pos2])
{
dp[i][pos]=d1[pos1++];
}
else
{
dp[i][pos]=d2[pos2++];
}
if(dp[i][pos-1]!=dp[i][pos])pos++;//关键,去重
}
}
}
cout << dp[v][k] << endl;
}
return 0;
}