动态规划也是一种分治思想,但与分治算法不同的是,分治算法是把原问题分解为若干子问题,自顶向下,求解各子问题,合并子问题的解从而得到原问题的解。动态规划也是自顶向下把原问题分解为若干子问题,不同的是,然后自底向上,先求解最小的子问题,把结果存储在表格中,在求解大的子问题时,直接从表格中查询小的子问题的解,避免重复计算,从而提高算法效率。
什么时候要用动态规划?
如果要求一个问题的最优解(通常是最大值或者最小值),而且该问题能够分解成若干个子问题,
并且小问题之间也存在重叠的子问题,则考虑采用动态规划。
怎么使用动态规划?
1. 判题题意是否为找出一个问题的最优解
2. 从上往下分析问题,大问题可以分解为子问题,子问题中还有更小的子问题
3. 从下往上分析问题 ,找出这些问题之间的关联(状态转移方程)
4. 讨论底层的边界问题
5. 解决问题(通常使用数组进行迭代求出最优解)
例题:给你一根长度为 n 的金条,请把金条剪成 m 段 (m 和 n 都是整数,n>1 并且 m>1)每断金条的长度记为 k[0],k[1],…,k[m].请问 k[0] k[1]…*k[m]可能的最大乘积是多少?
思路:1.首先,根据题意我们可以知道金条的长度和段数都要大于1,也就是说段数要从2开始;2.又因为金条的长度和段数都是整数,所以段数的范围是2~n,比如一根10cm的金条,最多能分成10段,每段1cm;3.接下来我们找找关联的子问题,当金条的长度小于2时,无法继续划分,最大的乘积返回0;当金条的长度为2时,可分为2段,每段的长度为1,最大的乘积即为1;当金条的长度为3时,可分为2段,一段长度为1,一段长度为2,最大的乘积即为2;4.最后我们来找找当金条长度大于3时的最大的乘积有何规律。
通过计算我们可以发现,无论金条的长度多长,当金条的段数越大,每段金条的长度的乘积就越小,当段数为2时,乘积是最大的。因此我们只用观察当段数为2时,金条的长度和每段金条的长度的最大乘积的规律,在遍历每段金条的长度时只需遍历n/2个数,不用重复计算。
在这里我们以金条长度为8举例,
k[0]=0;
k[1]=1;
k[2]=2;
k[3]=3;
k[4]=4;
k[5]=6;
k[6]=9;
k[7]=12;
k[8]=18;
当k[8]时,系统将会依次遍历k[1]*k[7]=12,k[2]*k[6]=18,k[3]*k[5]=18,k[4]*k[4]=16,然后比较这些乘积的大小,最后得出最大的乘积,结果为18。
#include <iostream>
#include <Windows.h>
using namespace std;
long long getMaxMul(int n) {
if (n < 2) return 0;
if (n == 2) return 1;
if (n == 3) return 2;
long long * k = new long long[n+1];
k[0] = 0;
k[1] = 1;
k[2] = 2;
k[3] = 3;
for (int i = 4; i <= n; i++) {
long long max = 0;
for (int j = 1; j <= i / 2; j++) {
long long ret = k[j] * k[i-j];
if (max < ret) {
max = ret;
}
}
k[i]=max;
}
return k[n];
delete[] k;
}
int main() {
int n = 0;//n为金条长度
cout << "请输入金条的长度:";
cin >> n;
cout << "可能的最大乘积是:" << getMaxMul(n) << endl;
system("pause");
return 0;
}