题目分析
这道题要求第K大背包,很显然数组增加一维K,01背包的状态转移式
为dp[i][j] = max(dp[i-1][j], dp[i-1][j-vol[i]] + val[i]);
很明显可以看出dp[i][j]只与公式后面的2个状态有关,因此只要在推的过程中
保存中间值,然后取出前K大的存储在dp[i][j][1…K]之中即可;
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int maxn = 105;
int val[maxn],vol[maxn];
int dp[1005][35];
int main()
{
int T,N,V,K;
scanf("%d", &T);
while(T--){
scanf("%d%d%d", &N, &V, &K);
int i,j,k;
for(i = 1; i <= N; i++)
scanf("%d", &val[i]);
for(i = 1; i <= N; i++)
scanf("%d", &vol[i]);
int a[35],b[35];
memset(dp, 0, sizeof(dp));
for(i = 1; i <= N; i++)
{
for(j = V; j >= vol[i]; j--)
{
for(k = 1; k <= K; k++)
{
a[k] = dp[j-vol[i]][k] + val[i]; //大家从这里可以发现a[],b[]是单调不增的,不明白的多想一下,这是由背包的性质可以知道的
b[k] = dp[j][k];
}
a[k] = b[k] = -1;
int x, y, z;
x = y = z = 1;
while(x <= K && (a[y] != -1 || b[z] != -1)) //这步对于学过归并排序的人来说已经很明显了,就是归并排序合并的过程
{
if(a[y] > b[z])
dp[j][x] = a[y], y++;
else
dp[j][x] = b[z], z++;
if(dp[j][x] != dp[j][x-1])
x++;
}
}
}
printf("%d\n", dp[V][K]);
}
return 0;
}