动态规划问题的三个特点:
(1) 求一个问题的最优解(通常是求最值)
(2) 该问题能分成若干个问题的子问题
(3) 并且子问题之间还有重叠的更小的子问题
(4) 从上往下分析问题,从下往上解决问题
满足上面的条件可以考虑使用动态规划。求解动态规划问题时,总是从最小问题开始解决,并将已解决的子问题的最优解存储下来,并把子问题最优解组合逐步解决大的问题。
贪婪算法每一步都可做一个贪婪选择,基于该选择可以得到最优解,比如剪绳子问题中剪出一段长度为3的绳子就是每一步做出的贪婪选择。
面试题14:给你一根长度为n的绳子,请把绳子剪成m段
(m和n都是整数,n>1并且m>1)每段绳子的长度记为k[0],k[1],…,k[m].请问k[0]k[1]…*k[m]可能的最大乘积是多少?
例如,当绳子的长度为8时,我们把它剪成长度分别为2,3,3的三段,此时得到的最大乘积是18.
很可惜牛客网上没有把这个题放上去,只能自己在vs上做了。
解题思路:首先定义函数f(n)为把长度为n的绳子剪成若干段后乘积的最大值,在剪第一刀时,我们有n-1种选择,也就是说第一段绳子的可能长度分别为1,2,3…,n-1。因此f(n)=max(f(i)*f(n-i)),其中0<i<n。这是一个自上而下的递归公式。由于递归会有大量的不必要的重复计算。一个更好的办法是按照从下而上的顺序计算,也就是说我们先得到f(2),f(3),再得到f(4),f(5),直到得到f(n)。当绳子的长度为2的时候,只能剪成长度为1的两段,所以f(2) = 1,当n = 3时,容易得出f(3) = 2;
解题步骤:
(1) 判断长度小于4的情况,并返回对应值。
(2) 计算长度大于等于4时,子问题分解成小于4的长度的最优解,并保存下来。
(3) 用两层循环从下往上求解最优解f(n),第一层循环负责从4开始求最优解f(4),一直求到f(n),第二层循环负责求具体每一个最优解的值,通过对每种剪法一一比较就可以求出。
#include <stdlib.h>
#include <iostream>
using namespace std;
int GetMaxLength(int RLength);
int main() {
int RLength = 0, MaxLength = 0;
while (1)
{
cout << "plese input the length of the rope" << endl;
cin >> RLength;
if (RLength < 0)break;
MaxLength = GetMaxLength(RLength);
cout << "the MaxLength is:" << MaxLength << endl;
}
}
int GetMaxLength(int RLength)
{
int Max = 0;
if (RLength < 2)return 0;//判断长度小于4的情况
if (RLength == 2)return 1;
if (RLength == 3)return 2;
int * products = new int[RLength + 1];
products[0] = 0;//大于等于四的情况中,已经被分解为长度小于4的子问题的最优解,此时子问题已不需要再分解了,直接用数组中的结果。
products[1] = 1;//比如f(4)=f(3)*f(1)=3*1=3
products[2] = 2;
products[3] = 3;
for (int i = 4; i <= RLength; ++i)//从下往上循环求解最优解
{
Max = 0;//求最优解Max之前先置零
for (int j = 1; j <=i / 2; ++j)//通过一一比较求长度为i的最优解
{
int product = products[j] * products[i - j];
if (Max < product)//
Max = product;
products[i] = Max;
}
}
Max = products[RLength];
delete[]products;
return Max;
}
贪婪算法解题思路:当n>=5时,尽可能多剪长度为3的绳子,当剩下的绳子长度为4时,把绳子剪成两段长度为2的绳子.
解题步骤:(时间复杂度O(1),空间复杂度O(1))
(1) 判断长度小于4的情况,并返回对应值。
(2) 长度大于等于4时,计算能剪长度为3绳子的个数,还要考虑最后长度为4的情况,长度为则应剪为22>31.
(3) 计算长度为2的绳子数。
(4) 计算所有长度为2和3的绳子的乘积。
#include <stdlib.h>
#include <iostream>
using namespace std;
int GetMaxLength(int RLength);
int main() {
int RLength = 0, MaxLength = 0;
while (1)
{
cout << "plese input the length of the rope" << endl;
cin >> RLength;
if (RLength < 0)break;
MaxLength = GetMaxLength(RLength);
cout << "the MaxLength is:" << MaxLength << endl;
}
}
int GetMaxLength(int RLength)
{
if (RLength < 2)return 0;//判断长度小于4的情况
if (RLength == 2)return 1;
if (RLength == 3)return 2;
int timesOf3 = RLength / 3;//尽可能剪长度为3的绳子
if (RLength - timesOf3 * 3 == 1)//若剩余最后一段为4,最好剪成2*2>1*3
timesOf3 -= 1;
int timesOf2 = (RLength-timesOf3*3)/2;//求长度为2的绳子数
return (int)(pow(3, timesOf3)*(int)(pow(2, timesOf2)));//返回所有长度为2和3的绳子的乘积
}