1. 程式人生 > >動態規劃or貪心演算法--剪繩子/切割杆

動態規劃or貪心演算法--剪繩子/切割杆

需求一:

 剪繩子,將長度為n的繩子剪成若干段,求各段長度乘積的最大值

分析:

 1、動態規劃
    設f(n)代表長度為n的繩子剪成若干段的最大乘積,如果第一刀下去,第一段長度是i,那麼剩下的就需要剪n-i,那麼f(n)=max{f(i)f(n-i)}。而f(n)的最優解對應著f(i)和f(n-i)的最優解,假如f(i)不是最優解,那麼其最優解和f(n-i)乘積肯定大於f(n)的最優解,和f(n)達到最優解矛盾,所以f(n)的最優解對應著f(i)和f(n-i)的最優解。首先,剪繩子是最優解問題,其次,大問題包含小問題,並且大問題的最優解包含著小問題的最優解,所以可以使用動態規劃求解問題,並且從小到大求解,把小問題的最優解記錄在陣列中,求大問題最優解時就可以直接獲取,避免重複計算。
    n<2時,由於每次至少減一次,所以返回0。n=2時,只能剪成兩個1,那麼返回1。n=3時,可以剪成3個1,或者1和2,那麼最大乘積是2。當n>3時,就可以使用公式進行求解。
    f(4)=max{f(1)f(3), f(2)f(2)}
    f(5)=max{f(1)f(4), f(2)f(3)}
    ...
    f(n)=max{f(1)f(n-1), f(2)f(n-2), f(3)f(n-3), ..., f(i)(fn-i), ...}
    因為需要保證f(i)f(n-i)不重複,就需要保證i<=n/2,這是一個限制條件,求1~n/2範圍內的乘積,得到最大值
 2、貪心演算法
    n<2時,返回0;n=2時,返回1;n=3時,返回2
    根據數學計算,當n>=5時,2(n-2)>n,3(n-3)>n,這就是說,將繩子剪成2和(n-2)或者剪成3和(n-3)時,乘積大於不剪的乘積,因此需要把繩子剪成2或者3。並且3(n-3)>=2(n-2),也就是說,當n>=5時,應該剪儘量多的3,可以使最後的乘積最大。對於長度是n的繩子,我們可以剪出n/3個3,剩餘長度是1或者2,如果餘數是1,就可以把1和最後一個3合併成4,那麼4剪出兩個2得到的乘積是4,比1*3大,因此這種情況下,需要將3的個數減少1,變成兩個2;如果餘數是2,那麼無需做修改。
    可以得到最大的乘積是:3^timesOf3 * 2^timesOf2
    相比動態規劃,計算更簡便,但是需要一定的數學技巧。

需求二:

 切割杆,現有長度為n的杆,已知1~n長度對應的價值陣列是prices[n],將其切割,求碎片的最大價值。

分析:

 假設第一刀切下來是i,那麼剩下的是n-i,假設f(n)代表切割n所能獲得的最大價值,那麼f(n)=max{f(i)+f(n-i)},因此大問題可以分解成小問題,並且同剪繩子問題,大問題的最優解包括小問題的最優解,那麼可以求出小問題的最優解,存到陣列中,在求大問題最優解時就可以直接從陣列中獲取,最終獲得最優解。
 f(0)=0
 f(1)=prices[0]
 f(2)=max{prices[1], f(1)+f(1)}
 f(3)=max{prices[2], f(1)+f(2)}
 f(4)=max{prices[3], f(1)+f(3), f(2)+f(2)}
 ...
 f(n)=max{prices[n-1], f(1)+f(n-1), f(2)+f(n-2), ..., f(i)+f(n-i), ...}
 對於f(n),為了保證f(i)+f(n-i)不重複,需要保證i<=n/2,這也是迴圈次數限制條件,在1~n/2範圍內求解。

程式碼:

import java.util.*;

class Cut
{
	//剪繩子的動態規劃演算法
	public int cutRope1(int n){
		//異常處理
		if(n < 0)
			throw new IllegalArgumentException("Illegal Paramters");

		if(n < 2)
			return 0;
		if(n == 2)
			return 1;
		if(n == 3)
			return 2;

		//建立陣列儲存子問題最優解
		int[] mul = new int[n+1];
		mul[0]=0;
		mul[1]=1;
		mul[2]=2;
		mul[3]=3;
		for(int i = 4; i <= n; i++){
			int max = 0;
			for(int j = 1; j <= i/2; j++){
				int temp = mul[j]*mul[i-j];
				if(max < temp)
					max = temp;
			}

			mul[i] = max;
		}

		return mul[n];
	}

	//如果允許不剪操作,那麼受影響的只是n=1,n=2,n=3
	public int cutRope2(int n){
		if(n < 0)
			throw new IllegalArgumentException("Illegal Paramters");

		if(n == 0)
			return 0;
		if(n == 1)
			return 1;

		int[] mul = new int[n+1];
		mul[0]=0;
		mul[1]=1;
		for(int i = 2; i <= n; i++){
			int max = i;

			for(int j = 1; j <= i/2; j++){
				int temp = mul[j]*mul[i-j];

				if(max < temp)
					max = temp;
			}

			mul[i] = max;
		}

		return mul[n];
	}

	//貪心演算法
	public int cutRope3(int n){
		//異常處理
		if(n < 0)
			throw new IllegalArgumentException("Illegal Paramters");

		if(n < 2)
			return 0;
		if(n == 2)
			return 1;
		if(n == 3)
			return 2;

		int timesOf3 = n/3;
		
		//如果剩餘1,那麼將1和一個3組成4可得到最大乘積
		if(n - timesOf3*3 == 1)
			timesOf3 -= 1;

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

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

	//切割杆
	public int maxValue(int[] prices, int n){
		//異常處理
		if(prices == null || prices.length != n)
			throw new IllegalArgumentException("Illegal Paramters");

		if(prices.length == 0)
			return 0;

		//儲存小問題的最優解
		int[] value = new int[n+1];

		for(int i = 1; i <= n; i++){
			int max = prices[i-1];

			for(int j = 1; j <= i/2; j++){
				int temp = value[j]+value[i-j];

				if(max < temp)
					max = temp;
			}

			value[i] = max;
		}

		return value[n];
	}
}

class CutDemo{
	public static void main(String[] args){
		Scanner scan = new Scanner(System.in);
		Cut cut = new Cut();

		//測試剪繩子
		System.out.println("請輸入繩子的長度:");
		int len = scan.nextInt();
		System.out.println("長度"+len+"繩子剪成若干段(至少兩段)後的乘積最大值:"+cut.cutRope1(len));
		System.out.println("長度"+len+"繩子剪成若干段(可以不剪)後的乘積最大值:"+cut.cutRope2(len));
		System.out.println("長度"+len+"繩子剪成若干段(至少兩段)後的乘積最大值:"+cut.cutRope3(len));

		//測試切割杆
		System.out.println("請輸入杆的長度:");
		int n = scan.nextInt();

		int[] prices = new int[n];
		for(int i = 0; i < n; i++)
		{
			System.out.print("prices["+i+"] = ");
			prices[i] = scan.nextInt();
		}

		System.out.println("杆切割之後的最大價值為:"+cut.maxValue(prices, n));
	}
}