剪绳子

探讨了绳子剪切问题的两种算法解决方案——动态规划和贪心算法。动态规划通过构建状态转移方程逐步求解,而贪心法则遵循特定的剪切策略。文章详细解释了每种方法的实现过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目描述

给定一根长度为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。
此题提供动态规划和贪心算法两种解法:

动态规划

假设f(n)为长度为n的绳子剑成若干段后的最大乘积。那么如果我们只剪一刀,该绳子就会分为两部分,假设在i上剪断,我们再求出这两部分绳子的最大乘积f(i),f(n-i)。然后不断以相同的方式进行分割,可以得到f(n)=max(f(i)*f(n-i))。
我们可以用递归求出来,因为这是一个自上而下的式子,但递归有许多重复的计算,效率低。这题我们采用自下而上的方式,用动态规划来做。f(1),f(2),f(3)都很容易得到,然后我们可以推出f(4),f(5),然后一直往后推出至f(n)。

public int cutting(int length) {
		//考虑到至少要剪两刀(m>1),先将剪后的f(n)<n的情况作为特例排除
	    if(length<2)
	    	return 0;
	    if(length==2)
	    	return 1;
	    if(length==3)
	    	return 2;
	    int max=0;//存储当前最大的f(n)
	    int[] array = new int[length+1];//array[n] = f[n]
	    array[0]=0;
	    array[1]=1;
	    array[2]=2;
	    array[3]=3;
	    for(int i=4;i<=length;i++) {
	    	for(int f=1;f<=i/2;f++) {//绳子只需要剪前一半的就可以,剪i和剪n-i是一回事
	    		int pro = array[f]*array[i-f];
	    		if(pro>max)
	    			max=pro;
	    		array[i]=max;
	    	}
	    }
	    return array[length];
	}

贪心算法

当n<=3时,不再进行剪切,因为会比n小。
当n==4时,将n=2*2,因为只有两种剪法,剪成1,3比较小。
当n>=5时,我们要把所有的绳子都剪成2或者3,同时我们要尽可能的多剪长度为3的绳子,因为3(n-3)>=2(n-2),当剩余的小于5时就没有必要再剪。

public int cutting1(int length) {
		if(length<2)
			return 0;
		if(length==2)
			return 1;
		if(length==3)
			return 2;
		if(length==4)
			return 4;
		int max=1;
		while(length>=5) {
			length-=3;
			max*=3;
		}
		if(length!=0)
			max*=length;
		return max;
	}
### 贪心算法实现 贪心算法的思路是尽可能多地长度为3的绳子段,因为当绳子长度大于等于5时,长度为3的段可以获得更大的乘积。当剩下的长度为4时,将其成两个2的段,这样可以获得更大的乘积[^2]。 ```cpp #include <iostream> #include <cmath> class Solution { public: int cutRope(int number) { if(number < 2) return 0; if(number == 2) return 1; if(number == 3) return 2; int countOf3 = number / 3; if (number - countOf3 * 3 == 1) { countOf3--; return static_cast<int>(pow(3, countOf3)) * 4; } if (number - countOf3 * 3 == 2) { return static_cast<int>(pow(3, countOf3)) * 2; } return static_cast<int>(pow(3, countOf3)); } }; int main() { Solution sol; std::cout << sol.cutRope(10) << std::endl; // 输出 36 return 0; } ``` ### 动态规划实现 动态规划的思路是将绳子长度从1到n的所有可能法都计算出来,并存储在数组中。对于每个长度i,遍历所有可能的法j(从1到i-1),并计算j*(i-j)和dp[j]*(i-j)的乘积,取最大值作为dp[i]的值[^1]。 ```cpp #include <iostream> #include <vector> #include <algorithm> class Solution { public: int cutRope(int number) { if (number < 2) return 0; if (number == 2) return 1; if (number == 3) return 2; std::vector<int> dp(number + 1, 0); for (int i = 1; i <= number; ++i) { for (int j = 1; j < i; ++j) { dp[i] = std::max(dp[i], std::max(j * (i - j), j * dp[i - j])); } } return dp[number]; } }; int main() { Solution sol; std::cout << sol.cutRope(10) << std::endl; // 输出 36 return 0; } ``` ### 总结 - **贪心算法**:适用于较大的绳子长度时间复杂度为O(1),但要数学推导来证明最优解。 - **动态规划**:适用于较小的绳子长度时间复杂度为O(n^2),但不要数学推导。 两种方法都可以有效地解决绳子题,选择哪种方法取决于具体的应用场景和对时间复杂度的要求。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值