一个改变的例子(某种程度)。找到最小的权重组合

时间:2013-12-04 01:09:52

标签: c recursion dynamic-programming knapsack-problem

TLDR :(第1部分)需要打印出最佳的重量组合以达到目标体重。 (第2部分)不知道选择什么方法。 (第3部分)另外,递归不是我的朋友。

我不是要求解决方案,我只是在寻找方向。

第1部分 一些文字首先。

该计划的输入是:

  • 一些重量
  • 衡量自己
  • 我应该撰写的目标重量

总是必须有一个权重= 1,所以所有权重都可以精确组合。 我应该打印出最佳的权重组合,例如

  • 重量:4
  • 重量:1,3,7,10
  • 目标体重:4
  • 输出:2 x 7

第2部分

我想到的第一件事是无界背包问题,我将权重的所有值设置为“1”,然后我会在背包中寻找最低的值。问题是,当我想找到一篇优秀的文章/代码/视频/无论如何理解它时,我的编程技巧都没有达到那个水平,我的谷歌搜索技能也让我失望。

然后有人指出了改变生活的问题。问题是它通常使用一个数组,我期待非常大的数字,我不能分配一个size =目标权重的数组。此外,如果我不仅要求尽可能最低的权重数量,而且要确切的数量,那么它似乎需要相当多的魔力。

我现在的解决方案,好吗?

  1. 按权重降序排序
  2. 计算从贪婪算法产生的权重数
  3. 去除一个最大的重量并尝试在没有它的情况下组成重量
  4. 重复3,直到我删除所有“最大重量”或重量开始再次增长
  5. (对于权重= 1,3,7,10和目标= 14,贪婪会给我1 x 10 + 1 x 3 + 1 x 1,在第三步之后我会得到(0 x 10 +)2 x 7)

    我来了只有我需要在递归函数之外重复这一点(就像我一直在做,直到我意识到它仍然没有给我正确的结果)但我需要将循环移动到递归函数中。

    第3部分

    这就是我现在部分代码的外观:

     for ( i = 0; i < weights_cnt; i ++ )
        for ( j = 0; j <= weight / *(weights + i); j ++ )
        {
            *counter = 0;
            if ( (res = greedyMagicImproved(weights + i, weight / *(weights + i) - j, weight, counter, min)) == 0 || min > *counter ) break;
            else min = *counter;
        }
    

    我知道这是一团糟。 (我写过的第一个递归函数,对不起)

    int greedyMagicImproved (int * weights, int limit, int weight, int * counter, int min)
    {
        if ( *counter > min ) return 0;
        else if ( weight % *weights == 0 )
        {
            printf ("%d * %d\n", limit, *weights);
            *counter += limit;
            return *counter;
        }
        else if ( weight == 0 ) return *counter;
        else if ( weight / *weights )
        {           
            printf ("%d * %d + ", limit, *weights);
            *counter += limit;
            return (greedyMagicImproved(weights + 1, (weight - *weights * limit) / *(weights + 1), (weight - *weights * limit) % *weights, counter, min));
        }
        else return greedyMagicImproved(weights + 1, weight / *(weights + 1), weight, counter, min);
    
        }
    

    这个产生这样的东西:

    Number of weights:
    8
    Actual weights of weights:
    1 2 4 5 10 20 60 95
    Weights to be composed:
    124
    GREEDY = 1 * 95 + 1 * 20 + 1 * 5 + 1 * 4
    IMP = 1 * 95 + 1 * 20 + 1 * 5 + 1 * 4
    2 * 60 + 1 * 4
    6 * 20 + 1 * 4
    ... some more irrelevant results I'll deal with later
    28
    GREEDY = 1 * 20 + 1 * 5 + 1 * 2 + 1 * 1
    IMP = 1 * 20 + 1 * 5 + 1 * 2 + 1 * 1
    1 * 20 + 1 * 5 + 1 * 2 + 1 * 1
    1 * 20 + 1 * 5 + 1 * 2 + 1 * 1
    2 * 10 + 1 * 5 + 1 * 2 + 1 * 1
    5 * 5 + 1 * 2 + 1 * 1
    ... some more results as well
    

    虽然我在第一种情况下看到了正确的结果,但我没有在第二种情况下。 所以基本上,我的问题是:我是否应该尝试将循环部分移动到递归中(并且基本上将它再次写入,因为我不知道该怎么做)或者我应该去窃取/打包并进行更改?

1 个答案:

答案 0 :(得分:0)

这是DP配方:

w[i]i=0,1,...p为硬币重量,f(t,i)为仅使用t w[k]达到目标k >= i所需的硬币数量}}。如果没有可能的方法进行更改,那么f(t,i)就是无限的。有了这个我们知道

f(t,i) = min_{n} [ n + f(t - n * w[i], i + 1)  ]

在英语中,要仅使用硬币i, i+1, ... p以最少的硬币击中目标,请选择硬币n的所有可能数量i,然后使用相同的DP进行更改剩余金额仅使用硬币i+1,i+2,...,最后选择产生最小值的解决方案。

基本情况是常识。例如f(0,_) = 0。你不需要任何硬币来达到零目标。

如果T是问题目标,那么答案将是f(T,0)。

由于您不想要答案,我会让您将其转换为代码。如果权重按降序排序,你很可能会得到更快的答案。