动态规划之钢条切割
问题导入
假定某公司购买长钢条,将其切割为短钢条进行出售(切割工序本身没有成本支出)。考虑求出最佳的切割方案。
出售一段长度为i的钢条价格如下表:
长度i | 价格p |
---|---|
1 | 1 |
2 | 5 |
3 | 8 |
4 | 9 |
5 | 10 |
6 | 17 |
7 | 17 |
8 | 20 |
9 | 24 |
10 | 30 |
时间复杂度
长度为n的钢条共有2的n-1次方种不同的切割方案,因为在距离钢条左端i处,总是可以选择切割或不切割。所以普通方法(递归)解决此问题的时间复杂度为2的指数级别。
普通方法效率很低,是因为它反复求解相同的子问题。
动态规划则仔细安排求解顺序,对每个子问题只求解一次,并将结果保存下来。如果随后再次需要求解此子问题的解,只需查找保存的结果,而不必重新计算。因此,动态规划是付出额外的内存空间来节省计算时间。而时间上的节省可能是非常巨大的。
动态规划原理
动态规划与分治方法相似,都是通过组合子问题的解来求解原问题。
适合使用动态规划方法求解的最优化问题应该具备两个要素:
1.最优子结构:如果一个问题的最优解包含其子问题的最优解,就称此问题具有最优子结构轻型纸。
2.子问题重叠:问题的递归算法会反复地求解相同的子问题,而不是一直产生新的子问题。
问题求解
public class Cut {
public static void main(String[] args) {
//钢条价格数组
int[] p = {1,5,8,9,10,17,17,20,24,30};
method(p, 10);
}
public static void method(int[] p, int n){
//结果数组
int[] r = new int[n+1];
//切割方式数组
int[] s = new int[n+1];
for(int j = 1;j <= n;j++){
int q = -1; //此处-1的作用是"无穷小"
for(int i = 1;i <= j;i++){
if(q < p[i-1]+r[j-i]){
q = p[i-1]+r[j-i];
s[j] = i; //表示长度为j时,被切割后的左部分长度为i,而右部分如何切割则递归查找s数组
r[j] = q; //长度为j时的最大收益值
}
}
}
for (int count = 0;count <= n;count++){
System.out.println(r[count]);
System.out.println(s[count]);
}
}
}
算法主体部分为两层for循环,因此用动态规划解决此问题的时间复杂度为n的平方。