《剑指offer》面试题14:剪绳子

探讨了如何使用动态规划解决绳子剪裁问题,以实现剪裁后的绳子段乘积最大。通过Java和C++代码示例,解释了动态规划法的特点和应用,同时对比了贪婪算法的解决方案。

摘要生成于 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。


思路:

其实这就是动态规划法,以下是动态规划法的几个特点:

1.求一个问题的最优解

2.整体问题的最优解依赖各子问题的最优解

3.小问题之间还有相互重叠的更小的子问题

4.为了避免小问题的重复求解,采用从上往下分析和从下往上求解的方法求解问题

java参考代码如下:

package chapter2;

public class P96_CuttingRope {
    public static int maxCutting(int length){
        if(length<2) return 0;
        if(length==2)return 1;
        if(length==3)return 2;
        int[] dp = new int[length+1];
        dp[0]=0;//仅仅为后续迭代服务,看成单独长度,与上面返回的值不冲突
        dp[1]=1;
        dp[2]=2;
        dp[3]=3;
        for(int i=4;i<=length;i++){
            for(int j=1;j<=i/2;j++) {
                dp[i] = Math.max(dp[i], dp[j] * dp[i - j]);
            }
        }
        return dp[length];
    }
    public static void main(String[] args){
        for(int i=2;i<10;i++){
            System.out.println("长度为"+i+"的最大值->"+maxCutting(i));
        }
    }
}

C++参考代码如下:

int maxProductAfterCutting_solution1(int length)
{
	if(length<2) return 0;
	if(length==2) return 1;
	if(length==3) return 2;

	int* products=new int[length+1];
	 products[0]=0;
	 products[1]=1;
	 products[2]=2;
	 products[3]=3;

	int max=0;
	for(int i=4;i<=length;++i)
	{
		max=0;
		for(int j=1;j<=i/2;++j)
		{
			int product=products[j]*products[i-j];
			if(max<product)
				max=product;
			product[i]=max;
		}
	}

	max=product[length];
	delete[] products;

	return max;
}

在上述代码中,子问题的最优解存储在数组products里。数组中第i个元素表示把长度为i的绳子剪成若干段之后各段长度乘积的最大值,即f(i)。我们注意到代码中第一个for循环变量i是顺序递增的,这意味着极端顺序是自下而上的。因此在求f(i)之前,对于每一个j(0<i<j),f(j)都已经求解出来了,并且结果保存在projects[j]里。为了求解f(i),我们需要求所有可能的f(j)*f(i-j)并比较得出他们的最大值。这就是代码中第二个for循环的功能。

贪婪算法思想的参考代码:

int maxProductAfterCutting_solution2(int length)
{
	if(length<2) return 0;
	if(length==2) return 1;
	if(length==3) return 2;

	//尽可能多的剪去长度为3的绳子段
	int timesOf3=length/3;

	//当绳子最后剩下的长度为4的时候,不能再剪去长度为3的绳子段。
	//此时更好的方法是把绳子段剪成长度为2的两段,因为2x2>1x3
	if(length-timesOf3==1)
		--timesOf3;

	int timesOf2=(length-timesOf3*3)/2;

	return (int)(pow(3,timesOf3))*(int)(pow(2,timesOf2));
}

接下来我们证明这种思路的正确性。首先,当n>=5的时候,我们可以证明2(n-2)>n并且3(n-3)>n。也就是说,当绳子剩下的长度大于等于5的时候,我们就把它剪成长度为3或者2的绳子段。另外,当n>=5时,3(n-3)>2(n-2),因此我们应该尽可能多剪长度为3的绳子段。当n=4时,剪成2x2的两段。

测试用例

a.功能测试(绳子的初始长度大于5)。
b.边界值测试(绳子的初始长度分别为0、1、2、3、4)。

参考:

https://www.jianshu.com/p/63b780a3157a
https://www.cnblogs.com/yongh/p/9660802.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值