钢条切割问题:给定一段长度为n英寸的钢条和一个价格表pi(i=1,2,...,n)求切割钢条方案,使得销售收益rn最大。
注意,如果长度为n英寸的钢条的价格pn足够大,最优解可能就是完全不需要切割。
思路:先将钢条切成两条,有n-1种方案,每一种方案的最优解都等于两个子钢条的最优解。我们从这n-1个伪最优解再挑出最优的解了
以下是伪代码:
1 CUT-ROD(p,n)
2 if n == 0
3 return 0
4 q=负无穷
5 for i = 1 to n
6 q=max(q,p[i]+CUT-ROD(p,n-i))
7 return q
上面只用了分治策略,这个算法的性能是很差的T(n)=2n,在子问题的求解中很多都是重复的。
动态规划就是避免这些重复。一般有两个思路1.记录中间解
1 MEM-CUT-ROD
2 let r[0..n] be a new array
3 for i = 0 to n
4 r[i]=负无穷
5 return MEM-CUT-ROD-AUX(p,n,r)
6
7 MEM-CUT-ROD-AUX(p,n,r)
8 if r[n]>=0
9 return r[n]
10 if n==0
11 q=0
12 else q=负无穷
13 for i=1 to n
14 q=max(q,p[i]+MEM-CUT-ROD-AUX(p,n-i,r))
15 r[n]=q
16 return q
2.通过对问题求解顺序的合理安排,达到避免重复
1 BOTTOM-UP-CUT-ROD(p,n)
2 let r[0..n] be a new array
3 r[0]=0
4 for j=1 to n
5 q=负无穷
6 for i=1 to j
7 q=max(q,p[i]+r[j-i])
8 r[j]=q
9 return r[n]
下面的伪代码还保留了切割长度
1 EXTEND-BOTTOM-UP-CUT-ROD(p,n)
2 let r[0..n] and s[0..n] be new arrays
3 r[0]=0
4 for j = 1 to n
5 q=负无穷
6 for i =1 to j
7 if q < p[i]+r[j-i]
8 q=p[i]+r[j-i]
9 s[j]=i
10 r[j]=q
11 return r and s
12
13 PRINT-CUT-ROD-SOLUTION(p,n)
14 (r,s)=EXTEND-BOTTOM-UP-CUT-ROD(p,n)
15 while n >0
16 print s[n]
17 n=n-s[n]
使用动态规划方法求解的最优化问题应该具备两个要素:最优子结构和子问题重叠
c语言实现代码;
#define MinNum -200 #define MaxNum 20 #include<stdio.h> #include<stdlib.h> int Max(int a, int b) { return a > b ? a : b; } int CUT_ROD(int p[],int n) { int q; if(n == 0) return 0; q = MinNum; for(int i = 1 ; i <= n; i++) { q = Max(q,p[i] + CUT_ROD(p,n-i)); } return q; } int Bottom_UP_ROD(int p[],int n , int r[]) { int q; for(int i = 1; i <= n ; i++) r[i] = MinNum; r[0] = 0; for(int j = 1; j <= n ; j++) { q = MinNum; for(int i = 1 ; i <= j ;i++) { q = Max(q,p[i] + r[j-i]); } r[j] = q; } return r[n]; } int MEMOIZED_CUT_ROD_AUX(int p[],int n , int r[]) { int q; if(r[n] >= 0) return r[n]; if(n == 0) q = 0; else { q = MinNum; for(int i = 1 ; i <= n ; i++) q = Max(q,p[i] + MEMOIZED_CUT_ROD_AUX(p,n-i,r)); } r[n] = q; return q; } void MEMOIZED_CUT_ROD(int p[],int n) { int r[MaxNum]; for(int i = 1 ; i <= n ; i++) r[i] = MinNum; } void EXTEND_DOWN_UP_ROD(int r[],int s[],int p[], int n) { r[0] = 0; for(int j = 1 ; j <= n ;j++) { int q = MinNum; for(int i =1 ; i <= j ; i++) { if(q < p[i] + r[j-i]) { q = p[i] + r[j-i]; s[j] = i; } } r[j] = q; } } void Print_CUT_ROD_SOLUTION(int p[], int n , int s[]) { printf("----------------------------------\n"); printf("长度为 %d 的切割方案为\n",n); while(n > 0) { printf("%3d",s[n]); n = n - s[n]; } printf("\n"); } int main() { int p[] = {0,1,5,8,9,10,17,17,20,24}; int r[MaxNum]; int s[MaxNum]; EXTEND_DOWN_UP_ROD(r,s,p,9); for(int i = 1; i <= 9; i++) { printf("长度为%d的收益最大为:%d\n",i,r[i]); } Print_CUT_ROD_SOLUTION(p,9,s); system("pause"); return 0; }