剑指offer面试题14-剪绳子(动态规划和贪婪法)

动态规划问题的三个特点:
(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的绳子的乘积
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值