对于“切棒”问题:
给出一根长度为n英寸的棒,并包含一组价格,其中包含所有小于n的尺寸的价格。确定通过切割杆并出售零件可获得的最大值。 [link]
算法简介(CLRS)第366页为自底向上(动态编程)方法提供了此伪代码:
1. BOTTOM-UP-CUT-ROD(p, n)
2. let r[0 to n]be a new array .
3. r[0] = 0
4. for j = 1 to n
5. q = -infinity
6. for i = 1 to j
7. q = max(q, p[i] + r[j - i])
8. r[j] = q
9. return r[n]
现在,我在理解第6行背后的逻辑时遇到了麻烦。为什么他们要用max(q, p[i] + r[j - i])
而不是max(q, r[i] + r[j - i])
?由于这是一种自下而上的方法,因此我们将首先计算r[1]
,然后计算r[2], r[3]...
,依此类推。这意味着在计算r [x]时,我们保证有r [x-1]。
r [x]表示我们可以为长度为x的一根棒获得的最大值(将其切成最大的利润后),而p [x]则为一根长度为x的棒的价格。第3-8行正在计算j = 1到n的值r[j]
,第5-6行通过考虑所有可能的切割来计算我们可以卖出长度为j的棒的最高价格。因此,在第6行中使用p [i]而不是r [i]有什么意义?如果在将长度切割为i后试图找到一根棒的最大价格,我们不应该加价r [i]和r [j-1]的关系?
我已经使用这种逻辑来编写Java代码,对于我尝试过的许多测试用例,它似乎都能提供正确的输出。在某些情况下,我的代码会产生不正确/无效的解决方案吗?请帮帮我。谢谢!
class Solution {
private static int cost(int[] prices, int n) {
if (n == 0) {
return 0;
}
int[] maxPrice = new int[n];
for (int i = 0; i < n; i++) {
maxPrice[i] = -1;
}
for (int i = 1; i <= n; i++) {
int q = Integer.MIN_VALUE;
if (i <= prices.length) {
q = prices[i - 1];
}
for (int j = i - 1; j >= (n / 2); j--) {
q = Math.max(q, maxPrice[j - 1] + maxPrice[i - j - 1]);
}
maxPrice[i - 1] = q;
}
return maxPrice[n - 1];
}
public static void main(String[] args) {
int[] prices = {1, 5, 8, 9, 10, 17, 17, 20};
System.out.println(cost(prices, 8));
}
}
答案 0 :(得分:2)
它们应该等效。
CLRS方法背后的直觉是,他们假设最后一根杆的长度为i
,因此其值恰好为p[i]
,因此他们试图找到单个“最后切割”。在此公式中,长度为i
的“最后一块”不会被进一步切割,而长度为j-i
的其余部分将被切掉。
您的方法将杆的所有分割都分为两部分,可以将其中的两个部分进一步切割。与CLRS方法相比,这认为是案例的超集。
这两种方法都是正确的,并且具有相同的渐近复杂度。但是,我认为CLRS解决方案更具“规范性”,因为它与DP解决方案的一种常见形式更紧密地匹配,在DP解决方案中,您只考虑了最后一个“事物”(在这种情况下,是最后一根未切割的杆)。
答案 1 :(得分:0)
我猜这两种方法都是正确的。
在我们证明它们都是正确的之前,先定义每种方法的确切作用
p [i] + r [j-i]将为您提供最大长度,您可以从长度为j的杆上获得该杆的最大值,该杆的尺寸为“ i”(无法进一步分割该杆)
r [i] + r [j-i]将为您提供最大长度i的棒的最大值,并且第一个切口的长度为“ i”(可以将两个块进一步分开)
现在考虑我们有一个长度为X的杆,并且解集将包含长度为k的段
由于k为0 在第二种方法中,您可以使用r [k] + r [X-k]找到相同的结果,因为我们知道r [k]将为> = p [k] 但是在您的方法中,由于您从两端切片了杆,因此可以获得更快的结果(一半的时间)
因此在您采用这种方法时,您可以将内部循环运行一半的长度就可以了。 但是我认为您的代码内部for循环中存在错误
应该是j> =(i / 2)而不是j> =(n / 2)