動態規劃是什麼:
每次決策依賴於當前狀態,又隨即引起狀態的轉移。一個決策序列就是在變化的狀態中產生出來的,所以,這種多階段最優化決策解決問題的過程就稱爲動態規劃。
動態規劃的思想與策略:
將待求解的問題分解爲若干個子問題(階段),按順序求解子階段,前一子問題的解,爲後一子問題的求解提供了有用的信息。在求解任一子問題時,列出各種可能的局部解,通過決策保留那些有可能達到最優的局部解,丟棄其他局部解。依次解決各子問題,最後一個子問題就是初始問題的解。
由於動態規劃解決的問題多數有重疊子問題這個特點,爲減少重複計算,對每一個子問題只解一次,將其不同階段的不同狀態保存在一個二維數組中。
在上代碼之前說個例子:
假設你有1,2,3(元)三種種類的零錢,數量不限,你需要找零6元,請問有多少種方法?
很顯然,是7種。我們就來說一說這個思想。
第一類:只用1元或2元或3元的零錢去找零,均爲1種,共3種
第二類:只用1元,2元二種找零,會出現2種,
第三類:只用1元,3元二種找零,會出現1種,
第四類:用1元,2元,3元三種找零,會出現1種,
所以答案是7種辦法。我們換一個角度進
引用塊內容
行思考,將其分爲3類:
第一類:只能用1元去找零,共1種
第二類:可以用1元,2元找零,會出現4種,
第三類:可以用1元,2元,3元找零,會出現7種,
在這裏就會有疑問,第三類不是直接和問題一樣了嗎?但是不難發現的是:在第二類的結果中包含了第一類的結果,第三類的結果中也包含了第二類的結果。
第一類與第二類,第二類與第三類存在的差異就是所用的零錢種類數。
換一個角度看,可以將第三類分爲2種情況:
(1)使用3元零錢:3,1,1,1—–3,2,1—–3,3(三種方法)
(2)不使用3元零錢:與第二類相同
同理將第二類分爲同樣的2種情況:
(1)使用2元零錢:2,2,2—–2,2,1,1—–2,1,1,1,1(三種方法)
(2)不使用2元零錢:與第一類相同
第一類其實同樣可以分爲2種情況:
(1)使用1元零錢:1,1,1,1,1,1(1種情況)
(2)不使用1元零錢:0種
如果將問題依次解決,那麼最後一個問題的解就是問題初始的解,這也正是動態規劃的魅力所在。
上面這一段文字或許你沒讀的太懂,所以通過代碼示例進行進一步的解讀。
public class 動態規劃_找零問題 {
public static void main(String[] args) {
Exchange exchange = new Exchange();
int a[] = {1,2,3};//零錢面值
int count = exchange.countWays(a, 3, 6);//使用前3種零錢找零6元
System.out.println(count);
}
}
//penny存放零錢的數組 n所用數組零錢種類的數量 aim所需找錢數
class Exchange {
public int countWays(int[] penny, int n, int aim) {
//數據出錯
if(n == 0 || penny == null || aim < 0){
return 0;
}
/**
*二維數組的大小設定爲零錢的種類數*(找零數+1)
所加的1是找零0元
*/
int[][] pd = new int[n][aim+1];
//將找零0元的列初始化爲1(無論是多少零錢去找零0元,都不存在,所以都是一種情況)
for(int i=0;i<n;i++){
pd[i][0] = 1;
}
//由於最小找零是1,所以第0行的每一列都初始化爲1
/**就像這樣
* 0 1 2 3 4 5 6
*(1元)【1】 【1】 【1】 【1】 【1】 【1】 【1】
*/
for(int i = 1; penny[0] * i <= aim; i++){
pd[0][penny[0] * i] = 1;
}
/**
*現在假設dp[n][m]爲使用前n種貨幣湊成的m的種數,那麼就會有兩種情況:
*使用第n種貨幣:dp[n-1][m]+dp[n-1][m-peney[n]]
*不用第n種貨幣:dp[n-1][m],爲什麼不使用第n種貨幣呢,因爲penney[n] > m。(就好比你需要找零2元,假設你有一種3元的零錢,你就使用不到)
*這樣就可以求出當m>=penney[n]時 dp[n][m] = dp[n-1][m]+dp[n][m-peney[n]]
*否則,dp[n][m] = dp[n-1][m]
*/
for(int i = 1; i < n; i++){
for(int j = 0; j <= aim; j++){
if(j >= penny[i]){
pd[i][j] = pd[i-1][j] + pd[i][j-penny[i]];
}else{
pd[i][j] = pd[i-1][j];
}
}
}
return pd[n-1][aim];
}
或許還是會有一丁點的疑惑(那我就再仔仔細細的分析一波):
第二類(只用1元,2元):
- 如果需要找一元錢,則不需要用到2元錢。那麼和只用一元錢是一樣的,即dp[n][m] = dp[n-1][m]。是1種。
- 如果需要找兩元錢,不用到2塊錢,即一種。
用到2元錢。然後再用(1元,2元)去找0元錢。1種。
相加共2種。 - 如果需要找三元錢,不用到2塊錢,即一種。
用到2元錢。然後再用(1元,2元)去找1元錢。1種。
相加共2種。 - 如果需要找四元錢,不用到2塊錢,即一種。
用到2元錢。然後再用(1元,2元)去找2元錢(見上)。2種。
相加共3種。 - 如果需要找五元錢,不用到2塊錢,即一種。
用到2元錢。然後再用(1元,2元)去找3元錢(見上)。2種。
相加共3種。 - 如果需要找六元錢,不用到2塊錢,即一種。
用到2元錢。然後再用(1元,2元)去找4元錢(見上)。即dp[n][m-peney[n]],3種。
相加共4種。
第三類(用1,2,3):
- 如果需要找一元錢,則不需要用到3元錢。即dp[n][m] = dp[n-1][m]。是1種。
- 如果需要找兩元錢,不用到3塊錢,即2種。
- 如果需要找三元錢,不用到3塊錢,即2種。
用到3元錢。然後再用(前三種)去找0元錢。1種。
相加共3種。 - 如果需要找四元錢,不用到3塊錢,即3種。
用到3元錢。然後再用(前三種)去找1元錢。1種。
相加共4種。 - 如果需要找五元錢,不用到3塊錢,即3種。
用到3元錢。然後再用(1元,2元)去找2元錢(見上)。2種。
相加共5種。 - 如果需要找六元錢,不用到3塊錢,即4種。
用到3元錢。然後再用(前三種)去找3元錢(見上)。3種。
相加共7種。
那麼算到最後呢,數組是這樣子的:
0 1 2 3 4 5 6
(1元) a0 【1】 【1】 【1】 【1】 【1】 【1】【1】
(2元) a1 【1】 【1】 【2】 【2】 【3】 【3】【4】
(3元) a2 【1】 【1】 【2】 【3】 【4】 【5】【7】
如果還有疑問,對不起,我已經把能講的講完了。。。。。