01 背包的第 k 优解,再加一个维度。
就是用 dp[j][k]代表容量为 j 时第 k 大的价值。那么在内层循环再遍历一次 k,每次遍历中将“取”和“不取”两种情况放在一个数组里,遍历完 k 之后对这个数组排序去重,然后根据顺序更新 dp[j][k]。
#include<cstring>
#include<cstdio>
#include<cstring>
#include<cmath>
#include <vector>
#include<string>
#include<iostream>
#include<algorithm>
using namespace std;
int dp[1005][1005];
int v[1005], w[1005];
int vec[100005];
bool cmp(int x, int y){
return x>y;
}
int main(){
//freopen("a.txt", "r", stdin);
int n, W, k, t;
cin >> t;
while(t--){
memset(dp, 0, sizeof(dp));
memset(vec, 0, sizeof(vec));
scanf("%d %d %d", &n, &W, &k);
for(int i = 1; i <= n; i++)
scanf("%d", &v[i]);
for(int i = 1; i <= n; i++)
scanf("%d", &w[i]);
for(int i = 1; i <= n; i++){
for(int j = W; j >= w[i]; j--){
int cnt = 0;
for(int th = 1; th <= k; th++){
vec[cnt++] = dp[j][th];
vec[cnt++] = dp[j-w[i]][th] + v[i];
}
sort(vec, vec+cnt, cmp);
cnt = unique(vec, vec+cnt) - vec; //去重
for(int th = 1; th <= min(cnt, k); th++)
dp[j][th] = vec[th-1];
}
}
cout << dp[W][k] << endl;
}
return 0;
}
这种算法是 748MS,注意到 35 和 36 两行的 STL 的使用耗时太多,观察 32 和 33 两行的代码,其中 dp[j][th]是递减的,dp[j-w[i]][th]也是递减的,所以用两个数组分别存下这两组数,然后在 O(n)的时间就可以求出。节省了 STL 的时间。
这种方法只有 109MS。
#include<cstring>
#include<cstdio>
#include<cstring>
#include<cmath>
#include <vector>
#include<string>
#include<iostream>
#include<algorithm>
using namespace std;
int dp[1005][1005];
int v[1005], w[1005];
int s1[100005];
int s2[100005];
int main(){
//freopen("a.txt", "r", stdin);
int n, W, k, t;
cin >> t;
while(t--){
memset(dp, 0, sizeof(dp));
scanf("%d %d %d", &n, &W, &k);
for(int i = 1; i <= n; i++)
scanf("%d", &v[i]);
for(int i = 1; i <= n; i++)
scanf("%d", &w[i]);
for(int i = 1; i <= n; i++){
for(int j = W; j >= w[i]; j--){
for(int th = 1; th <= k; th++){
s1[th-1] = dp[j][th];
s2[th-1] = dp[j-w[i]][th] + v[i];
}
s1[k] = s2[k] = -1;
int cnt = 1, cnt1 = 0, cnt2 = 0;
while(cnt<=k && (s1[cnt1]!=-1 || s2[cnt2]!=-1)){
if(s1[cnt1]>s2[cnt2])
dp[j][cnt] = s1[cnt1++];
else dp[j][cnt] = s2[cnt2++];
if(dp[j][cnt] != dp[j][cnt-1]) cnt++;
}
}
}
cout << dp[W][k] << endl;
}
return 0;
}