1 使用动态规划求解问题的特点:
求解问题的最优解(最大值或者最小值)
这个问题可以分解成若干个子问题,整体问题的最优解依赖各个子问题的最优解
这些小问题间还有相互重叠的更小的子问题
从上往下分析问题,从下往上求解问题,这是动态规划解决问题的第四个特点,在第2节说明
例子:长度为n的绳子剪成m段(n>1且m>1且m,n都为整数),各段长度的乘积可能最大值是多少。
满足:特点一:求剪出各段绳子长度乘积的最大值,满足特点一
特点二:用f表示剪后的最大值,问题为求f(n),任意位置如i剪一刀,因为f(n)=f(i)*f(n-i),问题变为求两个子问题,满足第二个特点
特点三:如果n=10,最初在4剪一刀,然后4绳子中间剪一刀,6绳子剪为2和4,问题变为:
f(10)=f(4)*f(6)=[f(2) * f(2)]*[f(2) * f(4)]
则f(4)和f(6)存在重叠子问题f(2)
特点四:由于子问题在分解大问题过程重复出现,为避免重复求解,从下往上求解小问题并存储下来,以此为基础求解大问题
2 动态规划算法步骤
判断题目是否是动态规划可以求解的题目:题目是否有以下几个特性:
(1) 能否将问题分解为几个子问题
(2)子文提是否可以往下划分,是否有重叠子问题
(3)有的题目是有关键字:“多少种”,“最”
将题目抽象出来,用f(n)表示该问题的(最优)解,根据划分的子问题写出f(n)=.......(递推关系)
由下往上求解,设计循环,从f(0),f(1),...开始求解
返回f(n)
以上面题目为例,
注意区分,长度为2、3的绳子与将一段绳子分解到长为2、3子问题的区别
代码:
int maxProductAftercut(int length) {
if (length < 2) return 0;//题目要求至少剪一刀,剪不了
if (length == 2) return 1;//剪一刀最大乘积为1
if (length == 3) return 2;//剪若干段最大乘积为2
//定义存储数组
int *record = new int[length + 1];//0,1,2,3...,length
//record[i]表示长度为i的绳子被剪成若干段后的各段绳子乘积可能最大值
record[0] = -1;//用不到
record[1] = 1;//1为单位长度
record[2] = 2;//分解到长度为2,3的绳子的小问题,不剪才是最大值
record[3] = 3;
for (int i = 4; i <= length; i++) {//对长度为i的绳子
//开始剪
int max = 0;
for (int j = 1; j <= i / 2; j++) {
int tmp = record[j] * record[i - j];
if (tmp > max) max = tmp;
}
record[i] = max;
}
int max = record[length];
delete[] record;
return max;
}