使用0/1背包逻辑的无界背包的递归解决方案

时间:2018-05-07 20:20:11

标签: algorithm dynamic-programming knapsack-problem

在我为0/1背包和无边背包检查的所有DP解决方案中,解决方案方法总是这样定义:

0/1背包:通过采取第n项或排除第n项来最大化总价值。例如,0/1 knapsack

无界背包:通过将第n个项目视为最后选择的项目,或将(n-1)项目视为最后选择的项目等来最大化总值等。例如,{{3} }

但是无界背包也可以使用0/1背包的逻辑来解决,并进行微调。我的意思是,对于0/1背包,我们执行以下操作(使用第一个链接中的代码):

return max( val[n-1] + knapSack(W-wt[n-1], wt, val, n-1),
            knapSack(W, wt, val, n-1)
          );

请注意,在我们包含wt[n-1]的情况下,我们将数组的大小减少1.这意味着wt[n-1]现在已用尽,因此无法再次使用。但是如果在无限制的情况下,我们不会将数组大小减少1(这意味着可以再次使用wt[n-1]),稍微修改后的重复关系可以正常工作:

return max( val[n-1] + knapSack(W-wt[n-1], wt, val, n),
            knapSack(W, wt, val, n-1)
          );

为什么这种无边背包的方法从未在任何地方提到过?实际上unbounded knapsack它明确地说,我们不能使用与无限制的0/1背包相同的逻辑。摘录自该链接:

Observation:
I can never exhaust any item because there are an unbounded supply of items.
Therefore:
The technique used in the 0,1 knapsack problem cannot be used.

但我不能反驳我上面提到的解决方案不会起作用。这个想法来自here问题,你需要计算一定数量的方法来改变给定的数量,假设有无限的硬币供应。

所以我的问题是为什么我在这里提出的方法,从未用于无边背包或至少从未提及任何地方?任何人都可以帮助我证明或反驳这种方法吗?提前谢谢!

3 个答案:

答案 0 :(得分:0)

在每个递归调用中,该函数都会迭代所有可用的权重,以查看是否可以包含它,如果可以,则将其值累加到当前递归调用的max_val中。在调用结束时,如果我们能够得到一个值,那么它将返回一个标志,告知已找到解决方案,并将maxSoFar设置为max_value。

#include<bits/stdc++.h>
using namespace std;

// Returns the maximum value with knapsack of
// W capacity
int unboundedKnapsack(int W, int n, int val[], int wt[], int &maxValue)
{
    // dp[i] is going to store maximum value
    // with knapsack capacity i.
    if( W == 0)
        return true;
    int maxSoFar = INT_MIN;
    bool foundASeries = false;
    for(int i = 0; i < n; i++)
    {
        if(W >= wt[i])
        {
            maxValue  = 0;
            if(unboundedKnapsack(W-wt[i], n, val, wt, maxValue))
            {
                foundASeries = true;
                maxSoFar = max(maxSoFar, maxValue + val[i]);
            }
        }
    }
    maxValue = maxSoFar;
    return foundASeries;
}

// Driver program
int main()
{
    int W = 8;
    int val[] = {10, 40, 50, 70};
    int wt[] = {1, 3, 4, 5};
    int maxValue = 0;
    int n = sizeof(val)/sizeof(val[0]);

    cout << unboundedKnapsack(W, n, val, wt, maxValue)<<endl;
    cout<< "max value is "<<maxValue;
    return 0;
}

答案 1 :(得分:0)

如果你将在无界背包中使用这种方法,那么程序的空间复杂度将是 O(n*W),如果你将使用无处不在的方法,那么它将是 O(n+w) ,其中 n是没有。件数,W 是总重量。

答案 2 :(得分:0)

您不会在任何地方找到您提到的解决方案,因为您的解决方案不是动态编程方法。在您的代码中,它重新评估了动态编程中不应发生的子情况。 如果你愿意,你可以试试这个代码,与你的代码相同,但它遵循动态编程规则


def UnboundedKnapSack2(wt, val, n, capacity):
    global dp
    if dp[n][capacity] != -1:
        return dp[n][capacity]
    else:
        if n == 0 or capacity <= 0:
            dp[n][capacity] = 0
            return dp[n][capacity]
        elif wt[n - 1] <= capacity:
            dp[n][capacity] = max(
                val[n - 1] + UnboundedKnapSack(wt, val, n, capacity - wt[n - 1]),
                UnboundedKnapSack(wt, val, n - 1, capacity),
            )
            return dp[n][capacity]
        else:
            dp[n][capacity] = UnboundedKnapSack(wt, val, n - 1, capacity)
            return dp[n][capacity]

weight = [1, 3, 4, 5]
values = [10, 40, 50, 70]
capacity = 8
dp = [[-1 for i in range(capacity + 1)] for j in range(len(weight) + 1)]
print(UnboundedKnapSack2(weight, values, len(weight), capacity))

这不是您问题的唯一解决方案,还有其他完全不使用递归的动态编程代码。

def UnboundedKnapSack3(wt, val, capacity):
    n = len(wt)
    dp = [[0 for i in range(capacity + 1)] for j in range(n + 1)]
    for i in range(n + 1):
        for j in range(capacity + 1):
            if i == 0 or j == 0:
                dp[i][j] = 0
            elif j >= wt[i - 1]:
                dp[i][j] = max(val[i - 1] + dp[i][j - wt[i - 1]], dp[i - 1][j])
            else:
                dp[i][j] = dp[i - 1][j]
    return dp[-1][-1]


print(UnboundedKnapSack3([1, 3, 4, 5], [10, 40, 50, 70], 8))

使用您喜欢的任何一个,但确保不应重新计算重叠的子问题。