题目链接
题目
Bone Collector II
Time Limit: 5000/2000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 5761 Accepted Submission(s): 3041
Here is the link: http://acm.hdu.edu.cn/showproblem.php?pid=2602
Today we are not desiring the maximum value of bones,but the K-th maximum value of the bones.NOTICE that,we considerate two ways that get the same value of bones are the same.That means,it will be a strictly decreasing sequence from the 1st maximum , 2nd maximum .. to the K-th maximum.
If the total number of different values is less than K,just ouput 0.
Followed by T cases , each case three lines , the first line contain two integer N , V, K(N <= 100 , V <= 1000 , K <= 30)representing the number of bones and the volume of his bag and the K we need. And the second line contain N integers representing the value of each bone. The third line contain N integers representing the volume of each bone.
题意
跟hdoj-2602几乎一样,首先输入数据的组数,每组第一行先输入3个数,分别表示骨头的总数,背包的容量,以及k值。接下来2行分别是每个骨头的价值和重量。可以有多种不同的骨头收集方式,要求输出的不是最优解的价值,而是第k优解的价值。
题解
这个题大部分代码甚至就跟2602题一样,主要还是在第k优解这里有难度。这里博主也是不是特别理解,只能参考他人的博客写出来代码而已。这里简要分析一波,有疑问可以评论交流。我们在正常求解的时候获得的是最优解,或者说是第1优解。所以我们可以用dp[i][j]来表示前i件物品拿取总质量小于等于j时的最大价值。那我们现在要求第k优解,就可以得到状态:dp[i][j][k],滚动数组优化后可以写成:dp[v][k]。
仍然还是考虑原版的状态转移方程:dp[v] = max(dp[v],dp[v - w[i]] + v[i])。滚动数组的化简不再解释。也就是说,对于i物品有2种状态,拿或者不拿。max函数是取这2个状态的最大值,因为我们只需要最优解。如果这里令k = 2呢,也就是说我们要的是第二优秀的解:dp[v][k]。其实这里就需要判定4个状态,dp[v][1]、dp[v - w[i]][1]、dp[v][2]、dp[v - w[i]][2],从这些状态中,我们找到第二优秀的解,就是dp[v][2]的值。以此类推,求第k优解,就需要2*k个状态排序,将第k个值求出即可。
所以说,我们应该在原来2个for循环的基础上,增加一个k的循环,获取全部的2*k个状态,然后排序,一个一个赋值更新。最终就可以求出所有的状态,即:dp[0...v][1...k]。
可以看出下面的代码,由于是滚动数组,所以排序等操作应该在k的for循环内完成。
只要求第k优解,无论是01背包还是其他背包,都可以在增加一个k的情况下用这样的算法完成操作。
C++ 代码 1500ms
#include<iostream>
#include<vector>
#include<string>
#include<cmath>
#include<algorithm>
#include<map>
#include<set>
#include<utility>
#include<queue>
#include<sstream>
#include<iterator>
#define TIME std::ios::sync_with_stdio(false)
#define LL long long
#define MAX 233
#define INF 0x3f3f3f3f
using namespace std;
int T,m,n,k;
int v[110],w[110];
int dp[1010][35];
int Max(int a,int b){
return a > b ? a : b;
}
int Min(int a,int b){
return a < b ? a : b;
}
bool cmp(int a,int b){
return a > b;
}
int main() {
TIME;
cin >> T;
while(T--){
cin >> m >> n >> k;
for(int i = 0;i < m;i++){
cin >> v[i];
}
for(int i = 0;i < m;i++){
cin >> w[i];
}
memset(dp,0,sizeof(dp));
vector<int> th_v;
for(int i = 0;i < m;i++){
for(int j = n;j >= w[i];j--){
for(int th = 1;th <= k;th++){ // 将2*k个状态储存
th_v.push_back(dp[j][th]);
th_v.push_back(dp[j-w[i]][th] + v[i]);
}
sort(th_v.begin(),th_v.end(),cmp);
vector<int>::iterator it = unique(th_v.begin(),th_v.end());
th_v.erase(it,th_v.end()); // 排序并去重
for(int th = 1;th <= Min(th_v.size(),k);th++){
dp[j][th] = th_v[th-1];
} // 更新值
}
}
cout << dp[n][k] << endl;
}
system("pause");
return 0;
}