算法導論第15章 動態規劃

動態規劃應用於子問題重疊的情況,即不同的子問題具有公共的子子問題。

動態規劃的重點在於用表存儲子子問題的解,使得每個子子問題只用求解一次,避免重複計算。(空間換時間)

動態規劃通常用來求解最優化問題

動態規劃兩個特徵

1.子問題重疊

2.最優子結構

 

四個步驟來設計一個動態規劃算法

1.刻畫一個最優解的結構特徵(尋找最優子結構,然後利用這種子結構從子問題的最優解構造出原問題的最優解)

2.遞歸的定義最優解的值

3.計算最優解的值,通常採用自底而上的方法

4.利用計算出的信息構造最優解

LeetCode的最長迴文子串問題就是用到了動態規劃

15.1鋼條切割問題

給定一段長度爲n英寸的鋼條和一個不同長度鋼條價目表pi(i=1,2,...,n)求切割方案

最優解是把可以迭代原鋼條切成兩段的最大值來做到,但是如果可以把一段鋼條切割最大值存起來,下次調用它的時候就不用再迭代了。時間複雜度就從O(2^n)變爲O(n^2)。

鋼條切割問題滿足最優子結構性質:問題最優解由相關子問題的最優解構成,而這些子問題可以單獨求解。

提到這點是因爲有時最優解不是由相關子問題的最優解構成。

以下是解決這個問題的代碼:允許設置鋼條切割費用,返回割切後各鋼條長度

package cutSteel;
import java.util.*;
public class cutSteel {
	public static void main(String[] args) {
		int steelLength = 15;//steelLength爲原鋼條長度
		int cutPay = 3;//切割一次的費用
		int[] price = {0, 1, 5, 8, 9, 10, 17, 17, 20, 24, 30};
		int priceLength = price.length;
		int[] maxIncome = new int[steelLength+1];
		String[] cutPlan = new String[steelLength+1];// 以字符串形式存儲每次切割的長度
		for (int i = 0; i <= steelLength; i++) {
			if(i <= priceLength - 1) {//price數組範圍內的初始化爲price數組對應值
				maxIncome[i] = price[i];	
			} else {
				maxIncome[i] = -10000;//超出價目表範圍的,一定要切
			}
			cutPlan[i] = String.valueOf(i);// 初始化爲對應i
			for(int k = 0; k <= i/2; k++) {
				int Income = maxIncome[k] + maxIncome[i-k] - cutPay;
				if (Income > maxIncome[i]) {// 要用大於號,否則字符串內可能混入多的0,導致分解切割長度錯誤
					maxIncome[i] = Income;
					cutPlan[i] = cutPlan[k] + "," + cutPlan[i-k];
				}
			}
		}
		System.out.println(maxIncome[steelLength]);
		System.out.println(cutPlan[steelLength]);//也可以把string轉化爲char數組,再轉化爲數組輸出
	}	
}

15.2 矩陣鏈乘法

https://blog.csdn.net/qq_39980514/article/details/90209170

15.3 動態規劃原理

具有最優子結構:一個問題的最優解包含其子問題的最優解

具有重疊子問題:如果遞歸算法反覆求解相同的子問題。

通過存儲算出的子問題答案,來避免重複計算

算法有兩種

1.自頂向下的動態規劃算法(如果子問題空間中的某些子問題不必求解,則更快,因爲算的少)

2.自底而上的動態規劃算法(如果每個子問題都必須求解一次,則更快,因爲沒有遞歸調用的開銷)

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章