第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。 

考点

1.动态规划 :时间复杂度:O(N^2) 空间复杂度:O(n)

1.1 寻找一个问题的最优解

1.2 整体问题的最优解依赖各个子问题的最优解

1.3 各个子问题之间还有重叠的更小子问题

1.4 从上向下分析问题,从下往上求解问题。把已解决的子问题的最优解保存在数组中。

数学表示:f(n)=max(f(i)*f(n-i)),0<i<n

2.贪婪算法:

贪心算法总是作出在当前看来最好的选择。也就是说贪心算法并不从整体最优考虑,它所作出的选择只是在某种意义上的局部最优选择。当然,希望贪心算法得到的最终结果也是整体最优的。虽然贪心算法不能对所有问题都得到整体最优解,但对许多问题它能产生整体最优解。如单源最短路经问题,最小生成树问题等。在一些情况下,即使贪心算法不能得到整体最优解,其最终结果却是最优解的很好近似。 

贪心算法求解的问题一般具有两个重要性质:贪心选择性质和最优子结构性质。

(1)所谓贪心选择性质是指所求问题的整体最优解可以通过一系列局部最优解的选择,即贪心选择来达到。这是贪心算法可行的第一个基本要素,也是贪心算法与动态规划算法的主要区别。

(2)当一个问题的最优解包含其子问题的最优解时,称此问题具有最优子结构性质。问题的最优子结构性质是该问题可用动态规划算法或贪心算法求解的关键特征。

算法很简单:每步都采取最优的做法。你每步都选择局部最优解,最终得到的就是全局最优解。

思路

1.动态规划 

   //1. 根据题意,如果长度小于2,返回0 

    //2. 长度为2:1*1,返回1 

    //3. 长度为3:1*2,返回2 
    //4. 新建一个数组,保存最优解 

    //5. 定义max变量

    //6. 由下至上开始循环,从4开始,到length结束 
        //6.1 从头到尾遍历,寻找products[i]的最大值 
            //6.1.1 依次求出长度为i的最优解 
            //6.1.2 找到最大值

        //6.2 更新products[i] 

    //7.将max变量更新为所求长度的最优解

    //8.释放最优解数组的内存 
    //9.返回该长度的最优解  

int maxProductAfterCutting_solution1(int length)
{
	//1. 根据题意,如果长度小于2,返回0
	if (length < 2)
		return 0;

	//2. 长度为2:1*1,返回1
	if (length == 2)
		return 1;

	//3. 长度为3:1*2,返回2
	if (length == 3)
		return 2;
	
	//4. 新建一个数组,保存最优解
	int* products = new int[length + 1];
	products[0] = 0;
	products[1] = 1;
	products[2] = 2;
	products[3] = 3;

	//5. 定义max变量
	int max = 0;

	//6. 由下至上开始循环,从4开始,到length结束
	for (int i = 4; i <= length; i++)
	{	
		//6.1 从头到尾遍历,寻找products[i]的最大值
		for (int j = 1; j<= i / 2; j++)
		{
			//6.1.1 依次求出长度为i的最优解
			products[i] = products[j] * products[i - j];
			//6.1.2 找到最大值
			if (products[i] > max)
				max = products[i];
		}

		//6.2 更新products[i]
		products[i] = max;
	}

	//7.将max变量更新为所求长度的最优解
	max = products[length];

	//8.释放最优解数组的内存
	delete[] products;
	
	//9.返回该长度的最优解
	return max;

}

 2.贪婪算法

    //1.由题意,长度小于2时,返回0 

    //2. 长度等于2时,返回1*1 

    //3.长度等于3时,返回2*1 

    // 下面讨论长度大于等于4的情况,由于因为2*2>3*1,题目说至少剪一刀,所以等于4时,剪2*2  
    // 由数学证明可知,n>=5时,2(n-2)>n,3(n-3)>n,且3(n-3)>2(n-2),所以当长度大于等于5时,尽可能地剪长度为3的
    //4. 计算最多剪长度为3的个数 

    //5. 如果剩下长度为4,则剪2*2, 

    //6. 计算剪长度为2的个数 

    //7. 返回总和=3的指数次*2的指数次 

int maxProductAfterCutting_solution2(int length){

	//1.由题意,长度小于2时,返回0
	if (length < 2)
		return 0;

	//2. 长度等于2时,返回1*1
	if (length == 2)
		return 1;

	//3.长度等于3时,返回2*1
	if (length == 3)
		return 2;

	// 下面讨论长度大于等于4的情况,由于因为2*2>3*1,题目说至少剪一刀,所以等于4时,剪2*2 
	
	// 由数学证明可知,n>=5时,2(n-2)>n,3(n-3)>n,且3(n-3)>2(n-2),所以当长度大于等于5时,尽可能地剪长度为3的
	//4. 计算最多剪长度为3的个数
	int timesof3 = length / 3;

	//5. 如果剩下长度为4,则剪2*2,
	if (length - timesof3 * 3 == 1)
		timesof3-=1;

	//6. 计算剪长度为2的个数
	int timesof2 = (length - timesof3 * 3) / 2;

	//7. 返回总和=3的指数次*2的指数次
	return (int)pow(3, timesof3) * (int)pow(2, timesof2); 
}
原文地址:https://www.cnblogs.com/lightmare/p/10428843.html