【Java】 劍指offer(13) 剪繩子

本文參考自《劍指offer》一書,代碼採用Java語言。html

更多:《劍指Offer》Java實現合集  java

題目

  給你一根長度爲n繩子,請把繩子剪成m段(m、n都是整數,n>1而且m>1)。每段的繩子的長度記爲k[0]、k[1]、……、k[m]。k[0]*k[1]*…*k[m]可能的最大乘積是多少?例如當繩子的長度是8時,咱們把它剪成長度分別爲二、三、3的三段,此時獲得最大的乘積18。git

思路

  本題採用動態規劃或者貪婪算法能夠實現。一開始沒有思路時,能夠從簡單的狀況開始想,試着算如下比較短的繩子是如何剪的。github

  當n=1時,最大乘積只能爲0;面試

  當n=2時,最大乘積只能爲1;算法

  當n=3時,最大乘積只能爲2;數組

  當n=4時,能夠分爲以下幾種狀況:1*1*1*1,1*2*1,1*3,2*2,最大乘積爲4;ide

  往下推時,發現n≥4時,能夠把問題變成幾個小問題,即:若是把長度n繩子的最大乘積記爲f(n),則有:f(n)=max(f(i)*f(n-1)),0<i<n。因此思路就很容易出來了:從下往上推,先算小的問題,再算大的問題,大的問題經過尋找小問題的最優組合獲得。post

  其實這就是動態規劃法,如下是動態規劃法的幾個特色:測試

  1.求一個問題的最優解

  2.總體問題的最優解依賴各子問題的最優解

  3.小問題之間還有相互重疊的更小的子問題

  4.爲了不小問題的重複求解,採用從上往下分析和從下往上求解的方法求解問題

 

  貪婪算法依賴於數學證實,當繩子大於5時,儘可能多地剪出長度爲3的繩子是最優解。

測試用例

  1.功能測試(長度大於5)

  2.邊界值測試(長度1,2,3,4)

完整Java代碼

(含測試代碼,測試代碼參考:CuttingRope.cpp

/**
 * 
 * @Description 面試題14:剪繩子
 *
 * @author yongh
 * @date 2018年9月17日 上午9:37:41
 */

// 題目:給你一根長度爲n繩子,請把繩子剪成m段(m、n都是整數,n>1而且m≥1)。
// 每段的繩子的長度記爲k[0]、k[1]、……、k[m]。k[0]*k[1]*…*k[m]可能的最大乘
// 積是多少?例如當繩子的長度是8時,咱們把它剪成長度分別爲二、三、3的三段,此
// 時獲得最大的乘積18。

public class CuttingRope {
	// ======動態規劃======
	public int maxProductAfterCutting_solution1(int length) {
		if (length <= 1)
			return 0;
		if (length == 2)
			return 1;
		if (length == 3)
			return 2;
		int[] product = new int[length + 1]; // 用於存放最大乘積值
		// 下面幾個不是乘積,由於其自己長度比乘積大
		product[0] = 0;
		product[1] = 1;
		product[2] = 2;
		product[3] = 3;

		// 開始從下到上計算長度爲i繩子的最大乘積值product[i]
		for (int i = 4; i <= length; i++) {
			int max = 0;
			// 算不一樣子長度的乘積,找出最大的乘積
			for (int j = 1; j <= i / 2; j++) {
				if (max < product[j] * product[i - j])
					max = product[j] * product[i - j];
			}
			product[i] = max;
		}
		return product[length];
	}

	// =======貪婪算法========
	public int maxProductAfterCutting_solution2(int length) {
		if (length <= 1)
			return 0;
		if (length == 2)
			return 1;
		if (length == 3)
			return 2;
		int timesOf3 = length / 3;
		int timesOf2 = 0;
		if (length - timesOf3 * 3 == 1) {
			timesOf3--;
			// timesOf2=2;  //錯誤!
		}
		timesOf2 = (length - timesOf3 * 3) / 2;
		return (int) (Math.pow(3, timesOf3) * Math.pow(2, timesOf2));
	}

	// =====測試代碼======
	void test(String testName, int length, int expected) {
		if (testName != null)
			System.out.println(testName + ":");
		if (maxProductAfterCutting_solution1(length) == expected) {
			System.out.print("    動態規劃:" + "passed  ");
		} else {
			System.out.print("    動態規劃:" + "failed  ");
		}

		if (maxProductAfterCutting_solution2(length) == expected) {
			System.out.println("貪婪算法:" + "passed  ");
		} else {
			System.out.println("貪婪算法:" + "failed  ");
		}
	}

	void test1() {
		test("test1", 1, 0);
	}

	void test2() {
		test("test2", 2, 1);
	}

	void test3() {
		test("test3", 3, 2);
	}

	void test4() {
		test("test4", 4, 4);
	}

	void test5() {
		test("test5", 5, 6);
	}

	void test6() {
		test("test6", 10, 36);
	}

	void test7() {
		test("test7", 50, 86093442);
	}

	public static void main(String[] args) {
		CuttingRope demo = new CuttingRope();
		demo.test1();
		demo.test2();
		demo.test3();
		demo.test4();
		demo.test5();
		demo.test6();
		demo.test7();
	}
}

  

test1:
    動態規劃:passed  貪婪算法:passed  
test2:
    動態規劃:passed  貪婪算法:passed  
test3:
    動態規劃:passed  貪婪算法:passed  
test4:
    動態規劃:passed  貪婪算法:passed  
test5:
    動態規劃:passed  貪婪算法:passed  
test6:
    動態規劃:passed  貪婪算法:passed  
test7:
    動態規劃:passed  貪婪算法:passed  
CuttingRope

 

收穫

  1.最優解問題,常常使用動態規劃法,關鍵要刻畫最優解的結構特徵(本題的f(n)),從下往上計算最優解的值,沒有思路時,從簡單狀況先算一下。

  2.動態規劃法中,子問題的最優解通常存放於一個數組中。

  3.本題貪婪規劃的代碼中,timeOf2別忘記等於1的狀況。

 

複習時補充:

1. 動態規劃法能夠直接令 f(n)=max{f(n-2)*2,f(n-3)*3} 就能夠了。

2. 貪婪算法,第51-58行可改成

		int timesOf3=n/3;
		if(n%3==0)
			return (int)Math.pow(3, timesOf3);
		if(n%3==1)
			return (int)Math.pow(3, timesOf3-1)*4;  //是乘以4,不是2
		return (int)Math.pow(3, timesOf3)*2;

  

更多:《劍指Offer》Java實現合集 

相關文章
相關標籤/搜索