背包动态规划解决方案的时间复杂度

时间:2014-06-15 21:54:00

标签: algorithm recursion complexity-theory dynamic-programming knapsack-problem

我看到了0-1背包问题here的递归动态编程解决方案。我记住了解决方案,并提出了以下代码。

private static int knapsack(int i, int W, Map<Pair<Integer, Integer>>, Integer> cache)
{
 if (i < 0) {
    return 0;
 }
Pair<Integer, Integer> pair = new Pair<>(i,W);
if (cache.contains(pair)) {
  return cache.get(pair)
}
int result = -1;
if (weights[i] > W) {
    result = knapsack(i-1, W);
} else {
    result = Math.max(knapsack(i-1, W), knapsack(i-1, W - weights[i]) + values[i]);
}
cache.put(pair, result);
return result;
}

有人可以向我解释为什么时间复杂度应为O(nW),其中n是项目数,W是权重限制。

4 个答案:

答案 0 :(得分:4)

是的,这是我不喜欢递归的原因之一。您几乎总是可以将递归算法重写为仅使用循环而不使用递归的算法。以下是您的算法仅适用于for循环的内容:

A[i,j]=0 for j=0, 1, ..., W

For i=1, 2, ..., n
    For j=0, 1, ..., W
        A[i,j]=max(A[i-1,j], A[i-1,j-weight[i]]+value[i] // or 0 if the index is invalid

Return A[n,W]

在这里,很明显复杂性为O(nW)。我将把这个代码与你的代码进行比较。

答案 1 :(得分:3)

如果您仔细考虑DP在表格式实现中的表格,那就更明显了。它在一个轴上有物品,在另一个轴上有最大可实现的重量,每个可能的整数重量有一行。对于某些重量组,必须密集填充表以找到最佳答案。这些相同的权重集将在备忘录哈希中需要Omega(nW)对,因为每个条目是一个恒定时间计算,同时计算所有。考虑如何获得昂贵的体重,这是一个很好的练习,所以我会告诉你。

答案 2 :(得分:3)

子问题图可以呈现递归动态规划算法。

子问题图由类似非重叠子问题的顶点组成。并且顶点中的有向边表示递归调用。边缘实际上代表了子问题的依赖性。

The runtime of the dynamic algorithm = (time to solve each subproblem)*(number of unique subproblems)

通常,

the cost = (outdegree of each vertex)*(number of vertices)

对于背包,

每个顶点的Outdegree最多为2 = O(1)。这是因为在每个子问题中,我们都试图用最多两种方式来解决它。

现在,如果我们检查子问题,我们可以找到一些模式,

The subproblem `(n,W)` depends on `(n-1,W)` and `(n-1,W-w(n))`.

`(n-1,W)` depends on `(n-2,W)` and `(n-2,W-w(n-1))`
`(n-1,W-w(n))` depends on `(n-2,W-w(n))` and `(n-2,W-w(n)-w(n-1))`

请注意,对于每个n项,权重最多可能会有1 to W。 (我们可以将这种扩展模式与动态斐波那契问题模式进行比较,并增加维度。)

因此,最多只有n*W个唯一的子问题。

因为我们只解决了一次子问题,

运行时间= O(1) O(n W)= O(n * W)。

答案 3 :(得分:-1)

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
public class Knapsack {
        double price;double kg;double pricePerKg;
Knapsack(double price,double kg, double pricePerKg){
    this.price=price;
    this.kg=kg;
    this.pricePerKg=pricePerKg;
}
public static List<Double> pricePerKg(List<Double> price,List<Double> kg){
    List<Double> pricePerKg = new ArrayList<Double>();
    for(int i=0;i<price.size();i++) {
        pricePerKg.add(price.get(i)/kg.get(i));
    }
    return pricePerKg;
}
public String toString() { return String.format("%s,%s,%s",price,kg,pricePerKg); }
    public static void main(String[] args) {
    List<Knapsack> list = new ArrayList<Knapsack>();
    double W=50;
    List<Double> price = new ArrayList<Double>();
    price.add(60.00);
    price.add(120.00);
    price.add(100.00);
    price.add(1000.00);
    ArrayList<Double> kg = new ArrayList<Double>();
    kg.add(10.00);
    kg.add(30.00);
    kg.add(20.00);
    kg.add(100.00);
    List<Double> pricePerKg =pricePerKg(price,kg);
    double weightCarry=0,currentWeight=0;
    double sum=0;
    for(int i=0;i<pricePerKg.size();i++) {
        list.add(new Knapsack(price.get(i),kg.get(i),pricePerKg.get(i)));
    }
    Collections.sort(list,new Comparator<Knapsack>() {
        @Override
        public int compare(Knapsack o1, Knapsack o2) {          
             return o1.pricePerKg > o2.pricePerKg ? -1 : o1.pricePerKg == o2.pricePerKg ? 0 : 1;
        }
    });
    System.out.println(list.toString().replaceAll(",", "\n"));
    for(int i=0;i<list.size();i++) {
        Knapsack li=list.get(i);
        weightCarry+=Double.valueOf(li.toString().split(",")[1]);
            if(weightCarry<W) {
                sum+=Double.valueOf(li.toString().split(",")[1])*Double.valueOf(li.toString().split(",")[2]);
              currentWeight=weightCarry;
            }
            else {
                sum+=(W-currentWeight)*Double.valueOf(li.toString().split(",")[2]);
            break;
            }
    }
    System.out.println("Fractional knapsack="+sum);
}
}

时间复杂度接近O(nlogn)