hdoj-2639 Bone Collector II 01背包的第K优解问题

版权声明:转载请注明原址,有错误欢迎指出 https://blog.csdn.net/iwts_24/article/details/80206100

题目链接

hdoj-2639 Bone Collector II

题目

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


Problem Description
The title of this problem is familiar,isn't it?yeah,if you had took part in the "Rookie Cup" competition,you must have seem this title.If you haven't seen it before,it doesn't matter,I will give you a link:

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.
 

Input
The first line contain a integer T , the number of cases.
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.
 

Output
One integer per line representing the K-th maximum of the total value (this number will be less than 2 31).
 

Sample Input
 
  
3 5 10 2 1 2 3 4 5 5 4 3 2 1 5 10 12 1 2 3 4 5 5 4 3 2 1 5 10 16 1 2 3 4 5 5 4 3 2 1
 

Sample Output
 
  
12 2 0
 

Author
teddy
 

Source

题意

跟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;
}

猜你喜欢

转载自blog.csdn.net/iwts_24/article/details/80206100