1 动态规划的思想
动态规划也是采取的分治的思想,关键点就在于怎么去分。
在钢管分割的问题上,可以这样去思考问题,假设钢管长度为N,最优解是将其分割为k段。那么当将钢管分割为2段时,
我们假设x = L(1) + L(2) + ... + L(p), y = L(p+1) + L(p+2) + ... + L(k)。则必然是L(1) + L(2) + ... + L(p)是
长度为x的最优解,L(p+1) + L(p+2) + ... + L(k)是长度为y的最优解。证明方法很简单,采取反证的思想,如果
L(1) + L(2) + ... + L(p)不是长度为x的最优解,那么L(1) + L(2) + ... + L(p) + L(p+1) + L(p+2) + ... + L(k)必然
不是长度为n的最优解。
所以说将n分割为x+y的时候,并且通过一定次数的继续分割,必然会产生诸如L(1) + L(2) + ... + L(p) + L(p+1) + L(p+2) + ... + L(k)
的最优解。也就是说可以通过2分法来产生最优解,但是2分法的两个局部最优解的和就不一定是全局最优解了。
根据以上思想,可以得出钢管最优解的一个递归公式
r(n) = max(pn,(r1 + r(n-1)),(r2 + r(n-2)),...(r(n-1), r1));
2 问题的简化
可以这么想,将钢条分为两段,第一段的长度依次递加,那么所以递归式可以简化为:
r(n) = max(pi + r(n-i)), 1<=i<=n;
3 问题的解决
<1>直接用根据递推公式建立递归函数
这样的缺点就是会重复计算价值,造成资源的浪费
<2>建立起包含价值数组的递归函数
这是针对<1>的缺点,加入了价值数组,那么在用到长度为n的最大价值时候,如果r[n]已经存在,就不需要再次求解
<3>自底向上的迭代方法求解
这个比较简单和容易实现,它是不断的根据已经有的解来求解未知的解,从而得到最终的答案
4 代码实现
动态规划也是采取的分治的思想,关键点就在于怎么去分。
在钢管分割的问题上,可以这样去思考问题,假设钢管长度为N,最优解是将其分割为k段。那么当将钢管分割为2段时,
我们假设x = L(1) + L(2) + ... + L(p), y = L(p+1) + L(p+2) + ... + L(k)。则必然是L(1) + L(2) + ... + L(p)是
长度为x的最优解,L(p+1) + L(p+2) + ... + L(k)是长度为y的最优解。证明方法很简单,采取反证的思想,如果
L(1) + L(2) + ... + L(p)不是长度为x的最优解,那么L(1) + L(2) + ... + L(p) + L(p+1) + L(p+2) + ... + L(k)必然
不是长度为n的最优解。
所以说将n分割为x+y的时候,并且通过一定次数的继续分割,必然会产生诸如L(1) + L(2) + ... + L(p) + L(p+1) + L(p+2) + ... + L(k)
的最优解。也就是说可以通过2分法来产生最优解,但是2分法的两个局部最优解的和就不一定是全局最优解了。
根据以上思想,可以得出钢管最优解的一个递归公式
r(n) = max(pn,(r1 + r(n-1)),(r2 + r(n-2)),...(r(n-1), r1));
2 问题的简化
可以这么想,将钢条分为两段,第一段的长度依次递加,那么所以递归式可以简化为:
r(n) = max(pi + r(n-i)), 1<=i<=n;
3 问题的解决
<1>直接用根据递推公式建立递归函数
这样的缺点就是会重复计算价值,造成资源的浪费
<2>建立起包含价值数组的递归函数
这是针对<1>的缺点,加入了价值数组,那么在用到长度为n的最大价值时候,如果r[n]已经存在,就不需要再次求解
<3>自底向上的迭代方法求解
这个比较简单和容易实现,它是不断的根据已经有的解来求解未知的解,从而得到最终的答案
4 代码实现
#include <iostream>
#include <algorithm>
#include <vector>
int cutRod(int* p, int n);
int memoryCutRod(int* p, int n);
int memoryCutRodAux(int* p, int n, std::vector<int>& r);
int bottomUpCutRod(int*p, int n);
int main()
{
int p[10] = {1,5,8,9,10,17,17,20,24,30};
int n = 4;
//first method
int r = cutRod(p, 4);
std::cout << "max r:" << r << std::endl;
//second method
r = memoryCutRod(p,4);
std::cout << "max r:" << r << std::endl;
//third mehod
r = bottomUpCutRod(p, 4);
std::cout << "max r:" << r << std::endl;
return 0;
}
int cutRod(int* p, int n)
{
if (n == 0)
return 0;
int q = INT_FAST32_MIN;
for (int i = 1; i <= n; i++)
{
q = std::max(q, p[i-1] + cutRod(p, n-i));
}
return q;
}
int memoryCutRod(int* p, int n)
{
std::vector<int> r(n + 1, INT_FAST32_MIN);
return memoryCutRodAux(p, n, r);
}
int memoryCutRodAux(int* p, int n, std::vector<int>& r)
{
if (r[n] >= 0)
return r[n];
int q = INT_FAST32_MIN;
if (n == 0)
q = 0;
else
{
for (int i = 1; i <= n; i++)
q = std::max(q, p[i-1] + memoryCutRodAux(p, n-i, r));
}
r[n] = q;
return q;
}
int bottomUpCutRod(int*p, int n)
{
std::vector<int> r(n + 1, INT_FAST32_MIN);
r[0] = 0;
for (int j = 1; j <= n; j++)
{
int q = INT_FAST32_MIN;
for (int i = 1; i <= j; i++)
{
q = std::max(q, p[i-1] + r[j-i]);
}
r[j] = q;
}
return r[n];
}
5 输出
max r:10
max r:10
max r:10
6 参考
算法导论