我看到了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是权重限制。
答案 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)