动态规划之钢条切割(算法导论)
1、问题描述
对于不同长度的钢条,其价格各不相同,现给定一个钢条的价格表,以及长度为n的钢条,求如何切割这个长度为n的钢条,使得价值最大。
2、样例分析
假如现在给定一价格表如下:
1 2 3 4 5 6 7 8 9 10
1 5 8 9 10 17 17 20 24 30
现有一个长度为4的钢条,如何切割才能使得价值最大?
总共有三种切割方案,分别为:
1、0+4,价值为9.
2、1+3,价值为1+8=9.
3、2+2,价值为5+5=10.
所以,切割方案为2+2时,所获得的价值最大。
3、问题解析
对于长度为n的钢条,假设最佳的切割点为:k,(1<k<=n).这时,将原问题转化成了两个规模更小的子问题,子问题的规模分别为:k和n-k。原问题的最优解切割方案依赖子问题的最优切割方案。然后依次找出子问题,子子问题的最优切割方案。
为了使问题变得更加简化,我们每次从钢条的左端切去长度为i的钢条,然后对剩下的长度为n-i的钢条继续进行切割。此时,对切下的长度为i的钢条不再进行切割,只对n-i进行切割。
在此,又有两种思路:
1)自顶向下的递归方案。每次在钢条的左侧切下长度为k的钢条,然后对右侧长度为n-k的钢条继续进行递归,找出最优的切割方案。这种方法存在一个问题,就是会反复求解相同的子问题。这时,需要将已经计算出的子问题结果进行保存,当下次需要求解相同子问题时,不需要重新进行计算。
2)自底向上的切割方案。依次计算长度为1,2,3,...,n的最优切割方案,第n次的最优切割方案,依赖于前n-1次的最优切割方案。
4、算法实现
对于以上的两种思路,分别将其进行了实现。其中函数CutSteel1使用自顶向下的递归方案来实现钢条的最优切割,而函数CutSteel2使用自底向上的钢条切割方案。两种方案的时间复杂度都为O(n2).
<span style="font-size:18px;">/** 钢条切割问题 给出一个价格表,如下: 1 2 3 4 5 6 7 8 910 (m = 10) 1 5 8 9 10 17 17 20 24 30 现有长度为n的钢条,求如何切割才能使得价值最大。 **/ #include <stdio.h> #include <string.h> int price[1002]; //up --> bottom int cutSteel1(int n, int r[], int s[]) //return the maxValue { int i, tmp, hel, ss; if(r[n] >= 0) return r[n]; if(0 == n) { tmp = 0; } else { for(i=1; i<=n; i++) { if(1 == i){ tmp = price[i]+cutSteel1(n-i, r, s); ss = i; } else{ hel = price[i] + cutSteel1(n-i, r, s); if(tmp < hel) { tmp = hel; ss = i; } } } s[n] = ss; // r[n] = tmp; } r[n] = tmp; return r[n]; } //bottom --> up int cutSteel2(int n, int r[], int s[]) { int i, j, tmp, hel, ss; r[0] = 0; s[1] = 1; for(i=1; i<=n; i++) { tmp = price[1]+r[i-1]; ss = 1; for(j=2; j<=i; j++) { hel = price[j]+r[i-j]; if(tmp < hel) { tmp = hel; ss = j; } } s[i] = ss; r[i] = tmp; } return r[n]; } void print(int n, int s[]) { int i; printf("切割方案: "); for(i=n; i>0; i=i-s[i]) printf("%d\t", s[i]); printf("\n\n"); } int main() { freopen("in.test", "r", stdin); int m, n; int i, maxValue; int r[1002], s[1002]; // input the price table memset(price, 0, sizeof(price)); scanf("%d", &m); for(i=1; i<=m; i++) scanf("%d",&price[i]); //input the length n while(scanf("%d", &n)==1) { memset(r, 0xffffffff, sizeof(r)); memset(s,0,sizeof(s)); maxValue = cutSteel1(n, r, s); //maxValue = cutSteel2(n, r, s); printf("n=%d, maxValue = %d\n", n, maxValue); print(n, s); } return 0; }</span>