剑指offer-剪绳子

算法要求

给你一根长度为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的绳子剪成若干段后各段长度乘积的最大值。在剪第一刀的时候,我们有n-1种可能的选择,也就是剪出来的第一段绳子的可能长度分别为1,2,…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;当绳子的长度为3时,可能把绳子剪成长度分别为1和2的两段或者长度都为1的三段,由于1×2>1×1×1,因此f(3)=2

另外一种解决的方案是:贪婪算法,也就是对某一个问题,希望得到最优解,我们先得到局部最优解,然后在得到全局最优解的一种算法。【此说法言简意赅,详情见百度】

  • 由于任何一段绳子,如果长度大于3的话,那么剪成的每一段长度为2,或是3的话,可以使乘积为最大值,
  • 先计算 绳子可以剪成长度为3 的有几段,之后计算可以剪成长度为2的有几段。

证明这种算法的正确性:当n >= 5 的时候,我们可以证明 2(n-2)> n 并且 3(n -3) > n。也就是说,当绳子大于5的时候,我们可以将绳子剪成2.或者3。而且当n >= 5的时候, 3(n -3) > 2(n-2),也就说,我们尽量剪成长度为3的绳子。之后在去剪长度为2的绳子。所得乘积或是最大的。
当长度为4的绳子,其实不剪绳子,此时乘积是最大的。但是题目要求至少要剪一刀。

算法实现

package com.offer.test;

/**  
 * 
 * 减绳子
 * 
 * 给你一根长度为n的绳子,请把绳子剪成m段(m,n都是大于1 的整数)。每段绳子长度的可能最大乘积是多少。
 * 使用 动态规划-贪婪算法
 * @author zhouwenchen@021.com  
 * @date 2019年6月17日 下午2:24:13 
 */
public class MaxProductAfterCuttingDemo {
	
	/**
	 * 使用的是动态规划
	 * @param len
	 * @return
	 */
	public static int axProductAfterCutting(int len) {
		if (len < 2) {
			return 0;
		}
		if (len == 2) {
			return 1;
		}
		if (len == 3) {
			return 2;
		}

		int[] result = new int[len + 1];
		result[0] = 0;
		result[1] = 1;
		result[2] = 2;
		result[3] = 3;

		// 自低向上开始求解
		int max = 0;
		for (int i = 4; i <= len; i++) {
			max = 0;
			for (int j = 0; j <= i / 2; j++) {
				int tempResult = result[j] * result[i - j];
				max = max < tempResult ? tempResult : max;
				result[i] = max;
			}
		}
		max = result[len];
		return max;
	}
	
	/**
	 * 贪婪算法的实现
	 * @param len
	 * @return
	 */
	public static int maxProductWithGreedy(int len) {
		if (len < 2) {
			return 0;
		}
		if (len == 2) {
			return 1;
		}
		if (len == 3) {
			return 2;
		}
		// 长度为3的有几段
		int timeOfThree = len / 3;

		// 判断还剩下多少,进行判断
		if (len - timeOfThree * 3 == 1) {
			timeOfThree -= 1;
		}
		// 计算长度为2的有几段
		int timeOfTwo = (len - timeOfThree * 3) / 2;
		
		// 计算每段乘积的最大值
		return (int) ((Math.pow(3, timeOfThree)) * (Math.pow(2, timeOfTwo)));
	}
	
	public static void main(String[] args) {
		System.out.println(axProductAfterCutting(9));// axProductAfterCutting(8)=18
//		System.out.println(maxProductWithGreedy(9)); // maxProductWithGreedy(8)=18
	}
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值