動態規劃典型例題解析

2021-09-04 10:56:03 字數 4074 閱讀 9641

動態規劃的主要思想是把問題劃分為乙個個子狀態,乙個狀態的最優解往往是基於其前乙個狀態的最優解。兩個狀態之間的關係,我們就稱之為狀態轉移方程。這裡引出了狀態和狀態轉移方程的概念:狀態是乙個當前的值,這個值是通過前乙個值以及狀態轉移方程推得的。在解決動態規劃問題的時候,我們往往會把問題建模為乙個一維陣列或是二維陣列,處理完邊界值之後,就可以通過前乙個狀態和後乙個狀態的遞推關係迴圈解出軸上的乙個個狀態值。如果說貪心演算法是為了達到目的追求區域性最優,簡單粗暴,那麼動態規劃就是一種相對嚴密,統籌全域性的演算法。

1、確定狀態:首先找到題目中變化的量,和求解目標對應的變數,一般我們可以基於這些量構建乙個一維或者二維陣列。

2、確定遞推方向:分析題目要求,確定求解陣列狀態的迴圈初始位置和遞推方向。

3、處理陣列邊緣值:定義陣列後,往往我們會把邊緣值置0。若目標值為0,對應的狀態的解自然也是0,邊界的0的部分需要我們主動加上去,大家也要格外注意一下dp下標和陣列下標因為引入0導致的偏移。

4、在迴圈中求解陣列:即通過迴圈,去遞推乙個個子狀態的值。這個時候需要注意狀態轉移方程,狀態之間的關係變化。

5、選出需要的最優解。

問題描述:給你若干面值為1,3,5,數量不限的錢幣,給乙個目標值,需要用最少的錢幣數量湊齊目標值。

解題思路:使用動態規劃,先確定湊0元需要0個錢幣,湊1元至少需要乙個1元錢幣;湊2元至少兩個1元錢幣;湊3元時選擇乙個3元硬幣比選擇3個1元硬幣更優,故選擇乙個3元硬幣;湊四元的時候我們選擇用到前面的結果,只要再加上一元硬幣即可,當然在選擇這個方案前我們比對過(4-1)元、(4-3)元、(4-5)元的幾種情況。由於之前的計算結果都已經最優,所以我們只要考慮最後一張錢幣拿的是一元、三元、還是五元即可。這樣一來,我們可以遞推得到任意狀態的解。

演算法實現:

/**

* 動態規劃1:錢幣選擇問題

*/public

void

dp1();

int sum=24;

int result =

dp1(money,sum)

; system.out.

println

(result);}

/** * 動態規劃1:錢幣選擇問題

* @return

*/private

intdp1

(int

money,

int sum)

for(

int i=

1;i<=sum;i++

)//迴圈獲取不同面值對應的最小數量

}return number[sum]

;}

題目描述:給你乙個類似二叉樹的結構,每個節點都有相應的值,現求自頂至底的路徑的最大數字和。

解題思路:其實這題自頂至底或者自低至頂都是可以的,我們這裡選擇前者來求解。我們只要把資料存入乙個二維陣列,然後從頂端往下依次推得到達每個節點的最大數字和。

演算法實現:

/**

* 動態規劃2:求三角形路徑數字最大和(自頂至底)

*/public

void

dp2()}

int sum =

dp2(a)

; system.out.

println

("最大和為:"

+sum);}

/** * 動態規劃2:求三角形路徑最大數字和(自頂至底)

* @param a

* @param i

* @param j

* @return

*/private

intdp2

(int

a)}return max;

}

題目描述:求某序列的最長非降子串行長度。

解題思路:設陣列上每個值為一條非降子串行的末端,這條子序列的長度就是陣列的狀態。每個狀態都與前乙個狀態有關。

演算法實現:

/**

* 動態規劃3:求最長非降子串行長度

*/public

void

dp3();

int length =

dp3(a)

; system.out.

println

(length);}

/** * 動態規劃3:求最長非降子串行長度(lcs)

* @param a

* @return

*/public

intdp3

(int

a)return length;

}

題目描述:給定兩個序列,求它們的最長公共子串行,子串行不要求連續(這裡的子串行可以理解為刪除序列上任意節點後餘下的部分組成的序列)

解題思路:這題也可以用動態規劃來解,這裡涉及到兩條序列,我們使用二維陣列,乙個角標來表示第乙個序列的某字元位置,另乙個角標來表示另乙個序列的某字元位置。狀態值的表示即兩個角標左側的兩個序列段的最長公共子串行長度。角標左移後得到的是當前狀態的前乙個狀態,比如當前a[i]=b[j],那麼如果令i和j都減一,那麼最長公共子串行長度減一;若a[i]!=b[j],且陣列a的角標左移後最長子串行會減小,那麼不能移動這個角標,而應該移動j,若兩段序列還有相同部分,那麼a[i]還會再次等於b[j]。因此,在這個題型裡我們依然可以找到子狀態之間的遞推關係。

演算法實現:

/**

* 動態規劃3:求最長非降子串行長度

*/public

void

dp3();

int length =

dp3(a)

; system.out.

println

(length);}

/** * 動態規劃3:求最長非降子串行長度(lcs)

* @param a

* @return

*/public

intdp3

(int

a)return length;

}

題目描述:給定若干物品,它們有一定的重量和價值,以及乙個有一定容量上限的揹包,物品每種型別只有乙個且在裝包時只可以選擇裝或者不裝,不能裝一部分。求揹包所能容納的最大價值。

解題思路:因為是物品只能選擇裝或者不裝,那麼就不適合採用貪心演算法了,此題我們用動態規劃來解。首先我們來找我們需要求解的狀態,這裡涉及到兩個變化的量,乙個是選擇物品的種類,乙個是揹包的容量(揹包的容量類似於我們之前求湊錢幣的題的那個總金額)。這題和例題1的區別是這題揹包可能會有一些空間剩餘,而例題一中必須要湊齊指定金額。在這題中,我們要求出在不同揹包容量下的最大價值,在某一定的容量下,還需要去考慮不同物品的選擇。假定我們只選擇物品a,然後求出在不同揹包容量下的最大價值,這顯然都是相同的。這時我們再加上物品b,再去推導不同揹包容量下的最大價值,這是受到物品a的影響的,我們要選擇是否放物品b。以此類推,我們可以得到在選擇任意物品和任意揹包容量下的最大價值。所以這題我們選擇二維陣列,表示不同種類物品數量的選擇和揹包的容量。

演算法實現:

/**

* 動態規劃5:01揹包問題(物品有重量和價值,且只有乙個,去放置到定容的包中)

*/public

void

dp5();

//重量

int[

] v =

;//價值

int m =8;

//揹包最大總容量

int n =3;

//物品種類數量

int value =

dp5(w,v,m,n)

; system.out.

println

(value);}

/** *

* 動態規劃5:01揹包問題

* @param w:物品重量

* @param v:物品價值

* @param m:揹包容量

* @param n: 物品種類數量

*/public

intdp5

(int

w ,

int[

] v ,

int m ,

int n)

if(dp[i]

[j]>max)

//篩選最大值}}

return max;

}

動態規劃典型例題

題目描述 一天,ykc在學校閒的無聊,於是決定上街買點吃的,ykc很懶,本來就不是很像逛街,於是找來了czl幫他買,這裡應該有滑稽,而czl也不願為ykc買東西吃,但是ykc很強勢,非讓他去買,呢沒辦法了,然而czl還有很多事要做,沒呢麼多時間幫ykc,而這條小吃街又很長,有n家店,n有50000這...

規劃模型的典型例題

規劃模型的典型例題 3 最優組隊問題 有七種規格的包裝箱要裝到兩輛平板車上。包裝箱的寬和高是一樣的,但厚度t 厘公尺 和重量w 公斤 是不同的。下表給出了每種包裝箱的厚度,重量以及數量。每輛平板車有10.2公尺的地方可用來裝包裝箱 象麵包片那樣 載重為40噸。由於地區貨運的限制,對c5,c6,c7類...

動態規劃例題

題目描述 有一條很長的數軸,一開始你在0的位置。接下來你要走n步,第i步你可以往右走ai或者bi。問n步之後,0到m的每個位置,能不能走到?輸入格式 第一行,兩個整數n,m。接下來n行,每行兩個整數ai,bi。輸出格式 一行,一共m 1個數,每個數都是0或1表示能否走到,數字之間不用空格隔開。樣例輸...