/* 钢条切割: 动态规划与分治的相同点:组合子问题求解原问题 不同点:分治的子问题不重叠,做了重复工作,动态规划保存解到表中 动态规划的特点: 1最优子结构:问题的最优解由相关子问题的最优解组合而成,子问题可以独立求解 动态规划的实现方式:1带备忘的自顶向下,2自底向上 1带备忘的自顶向下:递归中保存子问题的解,需要子问题的解时,首先检查是否已经保存过此解,如果是直接返回保存的值 2自底向上:定义子问题的规模,将子问题按照规模排序,按从小到大的顺序求解,求解子问题时,该子问题所依赖的更小的子问题已经保存 两个方法的区别: 自顶向下递归 自底向上无需递归,设置初始值,用两层循环: 1<= i <= n, 1<= j <= i , 用R[i] = max(R[i] , P[j] + R[i - j] ) 公司出售一段长度为i英寸的钢条价格为Pi(i=1,2,...),钢条长度为整英寸 长度i 1 2 3 4 5 6 7 8 9 10 价格Pi 1 5 8 9 10 17 17 20 24 30 给定长度为n的钢条,求使得的最大收益Rn 分析: 设Rn表示长度为n的钢条的最大收益,Pn表示长度为n的钢条的价格,那么实际上问题对Rn的求解, 从左边切分出长度为i的一段,只对右边n-i进行递归切割 例如:第一段长度为n,收益为Pn,剩余长度为0,收益R0=0, 递归方程: Rn=max1<=i<=n(Pi+Rn-i) 递归基:R0=0,P0=0 输入的第一行是钢条的长度n 输入: 4 输出: 10 */ #include <iostream> #include <string.h> using namespace std; const int MAXSIZE = 10000; const int priceArr[11] = {0 , 1 , 5, 8, 9, 10, 17 , 17 , 20 , 24 , 30}; int rArr[MAXSIZE]; //自定向下的备忘录方法 int R(int n) { //鲁棒性 if(n < 0) { return -1; } //长度为0的钢条收益为0 if(n == 0) { rArr[0] = 0; return 0; } //递归基 if(rArr[n] > 0) { return rArr[n]; } //递归步 else { int max = -1; for(int i = 1 ; i <= n ; i++) { int value = priceArr[i] + R(n-i); if(value > max) { max = value; } } rArr[n] = max; return rArr[n]; } } int max(int a, int b) { return a > b ? a : b; } //自底向上的方法,实际上是0-1背包的格式,R(j)=max(R(n), P[i] + R(j-i) ) , 1 <= j <= n , 1<= i <= j , R[0] =0,自底向上无需递归,用数组和两层循环解决 int R_downToUp(int n) { if(n < 0) { return -1; } //递归基,长度为0的收益为0 rArr[0] = 0; for(int i = 1 ; i <= n ; i++) { int q = -1; for(int j = 1 ; j <= i ; j++) { q = max( q , priceArr[j] + rArr[i - j] ); } rArr[i] = q; } return rArr[n]; } void process() { int n; while(cin >> n) { //初始化 for(int i = 0 ; i < MAXSIZE ; i++) { rArr[i] = -MAXSIZE; } rArr[0] = 0;//边界,长度为0的收益为0,易漏 //int iResult = R(n); int iResult = R_downToUp(n); cout << iResult << endl; } } int main(int argc, char* argv[]) { process(); getchar(); return 0; }