改进的动态背包 - 有问题的输入?

时间:2014-08-28 20:59:13

标签: c++ algorithm dynamic-programming knapsack-problem

以下是this SPOJ问题的尝试解决方案。输入是:

  1. 硬币中一定金额的总重量
  2. 使用货币的硬币的价值和相应的权重,目标是找到给定金额的最小可能货币价值。
  3. 我稍微修改了wikipedia article关于背包问题的背包问题的动态编程解决方案 - 我刚刚按重量对硬币进行了分类,所以我不必通过所有硬币来获取最小值和(希望)确保组合重量等于容量。 (请看代码,它非常简单并且评论过。)

    然而,根据判断,有一个输入,我的算法给出了错误的答案。你能否说一下这个算法有什么问题?

    #include <iostream>
    #include <vector>
    #include <algorithm>
    #include <limits>
    
    using namespace std;
    
    typedef unsigned int weight_t;
    typedef unsigned int value_t;
    
    struct coin {
        weight_t weight;
        value_t value;
    };
    
    coin make_coin(weight_t weight, value_t value) {
        coin ret;
        ret.weight = weight;
        ret.value = value;
        return ret;
    }
    
    bool compare_by_weight(const coin& coin1, const coin& coin2) {
        return coin1.weight < coin2.weight;
    }
    
    int main() {
        unsigned int test_cases;
        cin >> test_cases;
        while(test_cases--) {
            //Initialization
            unsigned int number_of_coins, n = 0;
            weight_t empty_pig, full_pig, coin_weight, coins_weight;
            value_t coin_value, min_value, current_value = 0;
            vector<coin> coins;
            vector<unsigned int> min_value_for_the_weight;
    
            //Input
            cin >> empty_pig >> full_pig;
            cin >> number_of_coins;
            n = number_of_coins;
            while(n--) {
                cin >> coin_value >> coin_weight;
                coins.push_back(make_coin(coin_weight, coin_value));
            }
    
            //Input processing
            coins_weight = full_pig - empty_pig;
            sort(coins.begin(), coins.end(), compare_by_weight);
            min_value_for_the_weight.resize(coins_weight+1);
            for(unsigned int i = 0; i < coins_weight; i++) min_value_for_the_weight[i] = 0;
    
            //For all weights
            for(unsigned int i = 1; i <= coins_weight; i++) {
                //Find the smallest value
                min_value = numeric_limits<value_t>::max();
                for(unsigned int j = 0; j < number_of_coins; j++) {
                    //The examined coin weights more or same than examined total weight and we either already have put a coin
                    //in, or this is the first one
                    if(coins[j].weight <= i && (min_value_for_the_weight[i - coins[j].weight] > 0 || i == coins[j].weight)){
                        current_value = coins[j].value + min_value_for_the_weight[i - coins[j].weight];
                        if(current_value < min_value) min_value = current_value;
                    } else break;  // <- this I deleted to get accepted
                }
                if(min_value == numeric_limits<value_t>::max()) min_value = 0;
                min_value_for_the_weight[i] = min_value;
            }
    
            //If the piggy empty, output zero
            if(!min_value_for_the_weight[coins_weight] && coins_weight != 0)
                cout << "This is impossible." << endl;
            else
                cout << "The minimum amount of money in the piggy-bank is " << min_value_for_the_weight[coins_weight] << "." << endl;
            }
        return 0;
    }
    

1 个答案:

答案 0 :(得分:2)

案例empty_pig == full_pig是有问题的,因为你忽略了重复min_value_for_the_weight的第0条的逻辑特殊大小。

另一个问题是,如果break coins[j].weight > i只是个好主意。当break和另一半合并为假时,旧代码可coin[j].weight <= i,即无法制作重量为i - coins[j].weight的硬币。这发生在以下测试用例中。

1
10 13
2
2 2
3 3

我们必须使用重量为2或3的硬币来制作重量3.重量1被正确地确定为不可能。正确地确定重量2是可能的。对于重量3,我们尝试重量为2的硬币,确定重量1是不可能的,并且在尝试重量-3硬币之前break。结果是代码错误地报告说不可能做出重量3。