《算法設(shè)計與分析基礎(chǔ)》(Python語言描述) 課件 第8章動態(tài)規(guī)劃_第1頁
《算法設(shè)計與分析基礎(chǔ)》(Python語言描述) 課件 第8章動態(tài)規(guī)劃_第2頁
《算法設(shè)計與分析基礎(chǔ)》(Python語言描述) 課件 第8章動態(tài)規(guī)劃_第3頁
《算法設(shè)計與分析基礎(chǔ)》(Python語言描述) 課件 第8章動態(tài)規(guī)劃_第4頁
《算法設(shè)計與分析基礎(chǔ)》(Python語言描述) 課件 第8章動態(tài)規(guī)劃_第5頁
已閱讀5頁,還剩173頁未讀 繼續(xù)免費(fèi)閱讀

下載本文檔

版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進(jìn)行舉報或認(rèn)領(lǐng)

文檔簡介

第8章保存子問題解—動態(tài)規(guī)劃8.1動態(tài)規(guī)劃概述8.2一維動態(tài)規(guī)劃CONTENTS提綱8.3二維動態(tài)規(guī)劃8.4三維動態(tài)規(guī)劃8.5字符串動態(tài)規(guī)劃8.6背包動態(tài)規(guī)劃8.7樹形動態(tài)規(guī)劃8.8區(qū)間動態(tài)規(guī)劃1/918.1動態(tài)規(guī)劃概述動態(tài)規(guī)劃將要解決的問題轉(zhuǎn)化為一系列的子問題并且逐步加以解決,將前面解決子問題的結(jié)果作為后續(xù)解決子問題的條件,并且避免無意義的窮舉。說明2/918.1.1從一個簡單示例入門【例8-1】一個樓梯有n(1≤n≤100)個臺階,上樓可以一步上1個臺階,也可以一步上2個臺階,設(shè)計一個算法求上樓梯共有多少種不同的走法。3/91解設(shè)f(n)表示上n個臺階的樓梯的走法數(shù),顯然,f(1)=1,f(2)=2(一種走法是一步上1個臺階、走2步,另外一種走法是一步上2個臺階)。對于大于2的n個臺階的樓梯:一種走法是第一步上1個臺階,剩余n-1個臺階的走法數(shù)是f(n-1);另外一種走法是第一步上2個臺階,剩余n-2個臺階的走法數(shù)是f(n-2)。所以有

f(n)=f(n-2)+f(n-1)。對應(yīng)的遞歸模型如下:f(1)=1f(2)=2f(n)=f(n-2)+f(n-1) n>24/911 deff1(n): #算法12 ifn==1:return13 elifn==2:return24 else:returnf1(n-2)+f1(n-1)f1(5)f1(3)f1(4)f1(1)f1(2)f1(3)f1(1)f1(2)f1(2)存在大量重復(fù)的子問題5/911 deff21(n): #被f2調(diào)用2 ifdp[n]!=0:returndp[n]3 ifn==1:dp[n]=14 elifn==2:dp[n]=25 else:dp[n]=f21(n-2)+f21(n-1)6 returndp[n]78 deff2(n): #算法29 globaldp10 dp=[0]*10511 returnf21(n)如何避免重疊子問題的重復(fù)計算呢?可以設(shè)計一個一維dp數(shù)組,用dp[i]存放f1(i)的值。遞歸算法6/91上述算法2采用遞歸算法,可以直接采用迭代實現(xiàn),仍然設(shè)計一維dp數(shù)組,用dp[i]存放f1(i)的值。1 deff3(n): #算法32 dp=[0]*105 #假設(shè)n的最大值不超過1053 dp[1]=14 dp[2]=25 foriinrange(3,n+1):6 dp[i]=dp[i-2]+dp[i-1]7 returndp[n]迭代算法7/91上述算法3就是動態(tài)規(guī)劃算法,其中數(shù)組dp(表)稱為動態(tài)規(guī)劃數(shù)組,從中看出動態(tài)規(guī)劃就是記錄子問題的結(jié)果再利用的方法。原問題子問題1子問題2子問題k…表原問題的解8/91f(1)f(n-2)f(n-1)f(n)…f(n)=f(n-2)+f(n-1)1 deff4(n): #算法42 dp=[0]*33 dp[0],dp[1]=1,24 foriinrange(2,n):5 dp[i%3]=dp[(i-1)%3]+dp[(i-2)%3]6 returndp[(n-1)%3]f(i)的值存放在dp[i-1]中滾動數(shù)組9/91最常見的算法1 deff5(n): #算法52 ifn==1:return13 elifn==2:return24 else:5 a,b,c=1,2,06 foriinrange(3,n+1):7 c=a+b8 a,b=b,c9 returnc10/918.1.2動態(tài)規(guī)劃的原理一個多段圖G=(V,E),在頂點(diǎn)0處有一水庫,現(xiàn)需要從頂點(diǎn)0鋪設(shè)一條管道到頂點(diǎn)9,邊上的數(shù)字表示對應(yīng)兩個頂點(diǎn)之間的距離,該圖采用鄰接矩陣A表示?,F(xiàn)要找出一條從頂點(diǎn)0到頂點(diǎn)9的線路,使得鋪設(shè)的管道長度最短。5343343633474236242012345678911/9153433436334742362420123456789階段0階段1階段2階段3階段4S0={0}S1={1,2,3}S2={4,5,6}S4={9}S3={7,8}分為5個階段,通常階段變量用k表示,這里k為0~4。階段k可能有多個狀態(tài),通常用狀態(tài)集合Sk表示,例如S1={1,2,3}。狀態(tài)變量xk表示Sk中某個狀態(tài),如x1可以取S1中的任意值。12/91動態(tài)規(guī)劃中當(dāng)前階段的狀態(tài)往往是上一階段狀態(tài)和相應(yīng)決策的結(jié)果,采用指標(biāo)函數(shù)表示它們之間關(guān)系稱為狀態(tài)轉(zhuǎn)移方程,指標(biāo)函數(shù)通常是最優(yōu)解函數(shù)。設(shè)最優(yōu)解函數(shù)f(s)為狀態(tài)s到終點(diǎn)9的最短路徑長度,用k表示階段。13/91狀態(tài)轉(zhuǎn)移方程:53433436334742362420123456789階段0階段1階段2階段3階段4S0={0}S1={1,2,3}S2={4,5,6}S4={9}S3={7,8}f4(9)=0fk(s)=min<s,t>∈E{A[s][t]+fk+1(t)} k從3到014/91從k=3開始直到k=0為止,f0(0)就是最短管道長度,稱為逆序解法。f4(9)=0fk(s)=min<s,t>∈E{A[s][t]+fk+1(t)} k從3到053433436334742362420123456789階段0階段1階段2階段3階段4S0={0}S1={1,2,3}S2={4,5,6}S4={9}S3={7,8}15/91逆序解法過程f4(9)=0f3(7)=A[7][9]+f4(9)=3f3(8)=A[8][9]+f4(9)=453433436334742362420123456789階段0階段1階段2階段3階段4S0={0}S1={1,2,3}S2={4,5,6}S4={9}S3={7,8}34016/91f2(4)=min(A[4][7]+f3(7)=6,A[4][8]+f3(8)=8)=6f2(5)=min(A[5][7]+f2(7)=9,A[5][8]+f2(8)=7)=7f2(6)=min(A[6][7]+f3(7)=6,A[6][8]+f2(8)=7)=653433436334742362420123456789階段0階段1階段2階段3階段4S0={0}S1={1,2,3}S2={4,5,6}S4={9}S3={7,8}6763417/91f1(1)=min(A[1][4]+f2(4)=13,A[1][5]+f2(5)=11)=11f1(2)=min(A[2][4]+f2(4)=9,A[2][5]+f2(5)=9,A[2][6]+f2(6)=10)=9f1(3)=min(A[3][4]+f2(4)=12,A[3][5]+f2(5)=9,A[3][6]+f2(6)=11)=953433436334742362420123456789階段0階段1階段2階段3階段4S0={0}S1={1,2,3}S2={4,5,6}S4={9}S3={7,8}676119918/91f0(0)=min(A[0][1]+f1(4)=13,A[0][2]+f1(2)=13,A[0][3]+f0(3)=12)=1253433436334742362420123456789階段0階段1階段2階段3階段4S0={0}S1={1,2,3}S2={4,5,6}S4={9}S3={7,8}11991219/91也可以設(shè)最優(yōu)解函數(shù)f(s)為起點(diǎn)0到狀態(tài)s的最短路徑長度狀態(tài)轉(zhuǎn)移方程:f0(0)=0fk(t)=min<s,t>∈E{fk-1(s)+A[s][t]} k從1到420/9153433436334742362420123456789108758243012這樣求解過程是從k=1開始直到k=4為止,f4(9)就是最短管道長度,稱為順序解法。f0(0)=0fk(t)=min<s,t>∈E{fk-1(s)+A[s][t]} k從1到421/918.1.3動態(tài)規(guī)劃求解問題的性質(zhì)和步驟最優(yōu)子結(jié)構(gòu):如果問題的最優(yōu)解所包含的子問題的解也是最優(yōu)的,就稱該問題具有最優(yōu)子結(jié)構(gòu)性質(zhì),即滿足最優(yōu)性原理。無后效性:如果某個階段狀態(tài)一旦確定,就不受以后決策的影響,也就是說某個狀態(tài)以后的決策不會影響以前的狀態(tài)。重疊子問題:一個問題分解的若干子問題之間是不獨(dú)立的,其中一些子問題在后面的決策中可能被多次重復(fù)使用。該性質(zhì)并不是動態(tài)規(guī)劃適用的必要條件,但是如果沒有這條性質(zhì),動態(tài)規(guī)劃算法同其他算法相比就不具備優(yōu)勢。采用動態(tài)規(guī)劃求解的問題一般要具有以下3個性質(zhì)22/91確定狀態(tài):將問題求解中各個階段所處的各種情況用不同的狀態(tài)表示出來。確定狀態(tài)轉(zhuǎn)移方程:描述求解中各個階段的狀態(tài)轉(zhuǎn)移和指標(biāo)函數(shù)的關(guān)系。確定初始條件和邊界情況:狀態(tài)轉(zhuǎn)移方程通常是一個遞推式,初始條件通常指定遞推的起點(diǎn),在遞推中需要考慮一些特殊情況,稱為邊界情況。確定計算順序:也就是指定求狀態(tài)轉(zhuǎn)移方程的順序,是順序求解還是逆序求解。消除冗余:如采用滾動數(shù)組進(jìn)一步提高時空性能。一般來說動態(tài)規(guī)劃算法設(shè)計要經(jīng)歷以下幾個步驟23/918.1.4動態(tài)規(guī)劃與其他方法的比較動態(tài)規(guī)劃的基本思想與分治法類似,也是將求解的問題分解為若干個子問題(階段),按照一定的順序求解子問題,前一個子問題的解有助于后一個子問題的求解。但分治法中各個子問題是獨(dú)立的(不重疊),而動態(tài)規(guī)劃適用于子問題重疊的情況。動態(tài)規(guī)劃又和貪心法有些相似,都需要滿足最優(yōu)子結(jié)構(gòu)性質(zhì),都是將一個問題的解決方案視為一系列決策的結(jié)果。不同的是貪心法每次采用貪心選擇便做出一個不可回溯的決策,而動態(tài)規(guī)劃算法中隱含有回溯的過程。24/918.2一維動態(tài)規(guī)劃一維動態(tài)規(guī)劃是指設(shè)計動態(tài)規(guī)劃算法中采用一維動態(tài)規(guī)劃數(shù)組,也稱為線性動態(tài)規(guī)劃。說明25/91給定一個含n(n≥1)個整數(shù)的序列,要求求出其中最大連續(xù)子序列的和。序列(-2,11,-4,13,-5,-2)的最大連續(xù)子序列和為20。序列(-6,2,4,-7,5,3,2,-1,6,-9,10,-2)的最大連續(xù)子序列和為16。規(guī)定一個序列最大連續(xù)子序列和至少是0,如果小于0,其結(jié)果為0。8.2.1最大連續(xù)子序列和26/91解dp[0]=a[0] 初始條件dp[i]=max(dp[i-1]+ai,ai) i>0

設(shè)計一維動態(tài)規(guī)劃dp,dp[i](0≤i≤n-1)表示以元素ai結(jié)尾的最大連續(xù)子序列和,顯然dp[i-1]表示以元素ai-1結(jié)尾的最大連續(xù)子序列和。判斷ai分為兩種情況:將ai合并到前面以元素ai-1結(jié)尾的最大連續(xù)子序列中,此時有dp[i]=dp[i-1]+ai。不將ai合并到前面以元素ai-1結(jié)尾的最大連續(xù)子序列中,即從ai開始構(gòu)造一個連續(xù)子序列,此時有dp[i]=ai。27/91dp[0]=-20-2dp[1]=11111dp[2]=72-4dp[3]=20313dp[4]=154-5dp[5]=135-2ans=dp[3]=20max求出dp中的最大元素ans。由于本題中最大連續(xù)子序列和至少為0(或者說最大連續(xù)子序列可以為空序列),所以最后的最大連續(xù)子序列和應(yīng)該為max(ans,0)。28/911 defmaxSubSum(a): #求最大連續(xù)子序列和2 globaldp3 n=len(a)4 dp=[0]*n5 dp[0]=a[0]6 foriinrange(1,n):7 dp[i]=max(dp[i-1]+a[i],a[i])8 ans=max(dp) #求dp中最大元素9 returnmax(ans,0)【算法分析】maxSubSum算法時間復(fù)雜度均為O(n),空間復(fù)雜度為O(n)。29/91先在dp數(shù)組中求出最大元素的序號maxi,i從maxi序號開始在a中向前查找,rsum從dp[maxi]開始遞減a[i],直到rsum為0,對應(yīng)的a中子序列就是一個最大連續(xù)子序列。dp[0]=-20-2dp[1]=11111dp[2]=72-4dp[3]=20313dp[4]=154-5dp[5]=135-2i=maxi=3rsum=7i=2rsum=11i=1rsum=0逆置max{11,-4,13}求出dp后推導(dǎo)出一個最大連續(xù)子序列30/911 defmaxSub(a): #求一個最大連續(xù)子序列2 n=len(a)3 x=[] #存放一個最大連續(xù)子序列4 maxi=dp.index(max(dp)) #求最大dp元素下標(biāo)5 rsum=dp[maxi]6 i=maxi7 whilei>=0andrsum!=0:8 rsum-=a[i]9 x.append(a[i])10 i-=111 x.reverse()12 returnx31/91空間優(yōu)化如果只需要求最大連續(xù)子序列和,可以采用滾動數(shù)組優(yōu)化空間。maxSubSum算法中用j標(biāo)識階段,由于dp[j]僅僅與dp[j-1]相關(guān),將一維dp數(shù)組改為單個變量dp。1 defmaxSubSum1(a): #求最大連續(xù)子序列和2 n=len(a)3 ifn==1:returna[0]4 dp=a[0]5 ans=dp6 forjinrange(1,n):7 dp=max(dp+a[j],a[j])8 ans=max(ans,dp)9 returnmax(ans,0)空間復(fù)雜度為O(1)32/91問題描述:給定一個含n(1≤n≤30000)個整數(shù)的數(shù)組

nums(整數(shù)值在-100000到100000之間),找到一個具有最大和的連續(xù)子數(shù)組(子數(shù)組最少包含一個元素),返回其最大和。

例如nums={-2,-1},結(jié)果為-1。

要求設(shè)計如下方法:

def

maxSubArray(self,

nums:

List[int])

->

int:8.2.2實戰(zhàn)—最大子序和(LeetCode53★)33/91解本題采用動態(tài)規(guī)劃求解的原理見8.2.1節(jié),這里僅僅需要求最大連續(xù)子序列和,而且該最大連續(xù)子序列至少含一個元素。采用滾動數(shù)組。1 class

Solution:2

def

maxSubArray(self,

nums:

List[int])

->

int:3

n=len(nums)4

if

n==1:return

nums[0]5

dp=nums[0]6

ans=dp7

for

j

in

range(1,n):8

dp=max(dp+nums[j],nums[j])9

ans=max(ans,dp)10

return

ans

#不能改為max(ans,0)34/91上述程序提交時通過,執(zhí)行用時為172ms,內(nèi)存消耗為30MB。35/91問題描述:給定一個無序的整數(shù)序列a[0..n-1],求其中最長遞增(嚴(yán)格)子序列的長度。例如,a={2,1,5,3,6,4,8,9,7},n=9,其最長遞增子序列為{1,3,4,8,9},結(jié)果為5。8.2.3最長遞增子序列36/91解dp[i]表示以a[i]結(jié)尾的最長遞增子序列的長度。計算順序是i從0到n-1,對于每個a[i],dp[i]置為1(表示只有a[i]一個元素時最長遞增子序列的長度為1)。37/91考慮a[0..i-1]中的每一個元素a[j],分為兩種情況:若a[j]<a[i],則以aj結(jié)尾的最長遞增子序列加上ai可能構(gòu)成一個更長的遞增子序列,此時有dp[i]=max(dp[i],dp[j]+1)。a0

aj

ai-1

ai

an-1dp[j]dp[i]=max(dp[i],dp[j]+1)aj<ai否則最長遞增子序列沒有改變。在求出dp數(shù)組后,通過順序遍歷dp求出最大值ans,則ans就是最長遞增(嚴(yán)格)子序列的長度。38/91狀態(tài)轉(zhuǎn)移方程dp[i]=1 0≤i≤n-1(初始條件)dp[i]=maxa[j]<a[i](j<i){dp[j]+1} 0≤i≤n-139/911 defmaxInclen(a): #求最長遞增子序列長度2 globaldp3 n=len(a)4 dp=[0]*n5 foriinrange(0,n):6 dp[i]=17 forjinrange(0,i):8 ifa[i]>a[j]:dp[i]=max(dp[i],dp[j]+1)9 ans=max(dp) #求dp中最大元素10 returnans【算法分析】時間復(fù)雜度為O(n2),空間復(fù)雜度為O(n)。40/91當(dāng)求出dp后可以推導(dǎo)出一個最長遞增子序列。先在dp數(shù)組中求出最大元素的序號maxj,置j=maxj,prej從j的前一個序號開始在a中向前查找,rnum從dp[maxj]開始,若a[prej]<a[j]置rnum--,直到rnum為0,對應(yīng)的a中子序列就是一個最大連續(xù)子序列。a0

aj

ai-1

ai

an-1maxjdp[i]=max(dp[i],dp[[j]+1)aj<ai41/911 defmaxInc(a): #求一個最長遞增子序列2 n,x=len(a),[] #x存放一個最長遞增子序列3 maxj=dp.index(max(dp)) #dp中最大元素下標(biāo)4 rnum=dp[maxj] #剩余的元素個數(shù)5 j=maxj #j指向當(dāng)前最長遞增子序列的元素6 x.append(a[j])7 prej=maxj-1 #prej查找前一個元素8 whileprej>=0andrnum!=0:9 ifa[prej]<a[j]anddp[prej]==rnum-1:10 rnum-=111 x.append(a[prej])12 j=prej;prej-=113 x.reverse() #逆置x14 returnx42/91假設(shè)有n個活動和一個資源,每個活動執(zhí)行時都要占用該資源,并且該資源任何時刻只能被一個活動所占用,一旦某個活動開始執(zhí)行,中間不能被打斷,直到其執(zhí)行完畢。每個活動i有一個開始時間bi和結(jié)束時間ei(bi<ei),它是一個半開時間區(qū)間[bi,ei),其占用資源的時間=ei-bi。假設(shè)最早活動執(zhí)行時間為0。求一種最優(yōu)活動安排方案,使得安排的活動的總占用時間最長。8.2.4*活動安排問題Ⅱ43/91解該問題與7.2.1節(jié)的活動安排問題Ⅰ類似,不同的是這里求一個總占用時間最長的兼容活動子集,而不是求活動個數(shù)最多的兼容活動子集,兩者是不同的。這里采用貪心法+動態(tài)規(guī)劃的思路,先求出每個活動A[i]的占用資源的時間A[i].length=A[i].e-A[i].b,將活動數(shù)組A[0..n-1]按結(jié)束時間遞增排序(貪心思路)。44/9111個活動(已按結(jié)束時間的遞增排列)活動i012345678910開始時間130535688212結(jié)束時間4567891011121315ans=1345/91設(shè)計一維動態(tài)規(guī)劃數(shù)組dp,dp[i]表示A[0..i](共i+1個活動)中所有兼容活動的最長占用時間。考慮活動i,找到前面與之兼容的最晚的活動j,即,稱活動j為活動i的前驅(qū)活動。如果活動i找到了前驅(qū)活動j,可以有兩種選擇:①活動j之后不選擇活動i,此時dp[i]=dp[i-1]。②活動j之后選擇活動i,此時dp[i]=dp[j]+A[i].length。兩種情況中取最大值。對應(yīng)的狀態(tài)轉(zhuǎn)移方程如下:dp[0]=活動0的時間

邊界情況dp[i]=max{dp[i-1],dp[j]+A[i].length} 活動j是活動i的前驅(qū)46/91

求出dp數(shù)組后,dp[n-1]就是最長的總占用時間。為了求一個最優(yōu)安排方案,設(shè)計一個一維數(shù)組pre,pre[i]的含義如下:①若活動i沒有前驅(qū)活動,置pre[i]=-2。②若活動i有前驅(qū)活動j,但不選擇活動i,置pre[i]=-1。③若活動i有前驅(qū)活動j,選擇活動i,置pre[i]=j。47/911 classAction: #活動類2 def__init__(self,b,e):3 self.b=b #活動起始時間4 self.e=e #活動結(jié)束時間5 self.length=e-b #求每個活動的占用時間6 def__lt__(self,other): #用于按e遞增排序7 ifself.e<other.e:returnTrue8 else:returnFalse48/9110 defplan(A): #動態(tài)規(guī)劃算法求dp11 globaldp,pre12 n=len(A)13 dp=[0]*n #初始化dp元素為014 pre=[-5]*n15 A.sort() #按e遞增排序16 dp[0]=A[0].length17 pre[0]=-2 #活動0沒有前驅(qū)活動49/9118 foriinrange(1,n):19 j=i-120 whilej>=0andA[j].e>A[i].b:j-=1

#找活動i的前驅(qū)j21 ifj==-1: #活動i前面沒有兼容22 dp[i]=A[i].length23 pre[i]=-2 #活動i沒有前驅(qū)24 else: #活動i存在前驅(qū)j25 ifdp[i-1]>dp[j]+A[i].length:26 dp[i]=dp[i-1]27 pre[i]=-1 #不選擇活動i28 else:29 dp[i]=dp[j]+A[i].length30 pre[i]=j #選活動i,前驅(qū)為j31 returndp[n-1]50/91【算法分析】主要時間花費(fèi)在查找前驅(qū)活動上,對應(yīng)的時間復(fù)雜度為O(n2)。51/911 defgetx(n): #求一個最優(yōu)方案2 x=[] #存放一個方案3 i=n-1 #從n-1開始4 whileTrue:5 ifi==-2:break #已經(jīng)沒有前驅(qū)活動了6 ifpre[i]==-1:i-=1 #不選擇活動i7 else: #選擇活動i8 x.append(i)9 i=pre[i]10 x.reverse() #逆置x11 returnx52/9111個活動求出的dp和pre如下所示。dp[10]=13活動i012345678910開始時間130535688212結(jié)束時間4567891011121315length326254434113dp[i]3266561010101113pre[i]-2-2-2-1-212-1-1-2853/91求一個最優(yōu)安排方案x活動i012345678910dp[i]3266561010101113pre[i]-2-2-2-1-212-1-1-28i=n-1=10pre[10]=8活動10√

xi=pre[10]=8pre[8]=-1活動8×i減1

i=7pre[7]=-1活動7×

i減1

i=6pre[6]=2活動6√

xi=pre[6]=2pre[2]=-2活動2√

xi=pre[2]=-2i=-2說明沒有前驅(qū)活動,結(jié)束x={2,6,10}1354/918.3二維動態(tài)規(guī)劃二維動態(tài)規(guī)劃是指設(shè)計動態(tài)規(guī)劃算法中采用二維動態(tài)規(guī)劃數(shù)組,也稱為坐標(biāo)型動態(tài)規(guī)劃。說明55/91給定一個高度為n的整數(shù)三角形,求從頂部到底部的最小路徑和及其一條最小路徑,從每個整數(shù)出發(fā)只能向下移動到相鄰的整數(shù)。例如,一個n=4的三角形,輸出的最小路徑和是13,一條最小路徑是2,3,5,3。23472659838.3.1三角形最小路徑和56/91解問題求解—自頂向下2347265983i-1,ji-1,j-1i,jJI234726598357/91

設(shè)計數(shù)組dp,dp[i][j]表示從頂部a[0][0]到達(dá)(i,j)位置的最小路徑和。起始位置只有(0,0),所以初始化為dp[0][0]=a[0][0]。這里有如下兩個邊界:對于j=0即第0列的任意位置(i,0),只有垂直方向到達(dá)的一條路徑,此時有dp[i][0]=dp[i-1][0]+a[i][0]。對于i=j即對角線上的任意位置(i,i),只有左斜方向到達(dá)的一條路徑,此時有dp[i][i]=dp[i-1][i-1]+a[i][i]。

234726598358/91其他兩條到達(dá)(i,j)位置的路徑,最小路徑和

dp[i][j]=min(dp[i-1][j-1],dp[i-1][j])+a[i][j]。i-1,ji-1,j-1i,jJI59/91dp[0][0]=a[0][0] 初始條件dp[i][0]=dp[i-1][0]+a[i][0] 第0列的邊界情況,1≤i≤n-1dp[i][i]=dp[i-1][i-1]+a[i][i] 對角線的邊界情況,1≤i≤n-1dp[i][j]=min(dp[i-1][j-1],dp[i-1][j])+a[i][j]

i>1的其他有兩條達(dá)到路徑在dp數(shù)組的第n-1行中求出的最小元素ans=dp[n-1][minj]。60/911 defminPathSum(a): #自頂向下求最小路徑和2 n=len(a)3 dp=[[0]*nforiinrange(n)] #二維動態(tài)規(guī)劃數(shù)組4 dp[0][0]=a[0][0]5 foriinrange(1,n): #考慮第0列的邊界6 dp[i][0]=dp[i-1][0]+a[i][0]7 foriinrange(1,n): #考慮對角線的邊界8 dp[i][i]=a[i][i]+dp[i-1][i-1]9 foriinrange(2,n): #考慮其他有兩條達(dá)到路徑的結(jié)點(diǎn)10 forjinrange(1,i):11 dp[i][j]=min(dp[i-1][j-1],dp[i-1][j])+a[i][j]12 ans=min(dp[n-1]) #求出dp[n-1]中最小元素ans13 returnans61/91那么如何找到一條最小和的路徑呢?設(shè)計一個二維數(shù)組pre,pre[i][j]表示到達(dá)(i,j)位置時最小路徑上的前驅(qū)位置,由于前驅(qū)位置只有兩個即(i-1,j-1)和(i-1,j),用pre[i][j]記錄前驅(qū)位置的列號即可。i-1,ji-1,j-1i,jpre[i][j]=j-1i-1,ji-1,j-1i,jpre[i][j]=j在求出ans后,通過pre[n-1][minj]推導(dǎo)求出反向路徑path,逆向輸出得到一條最小和的路徑。62/911 defminPathSum1(a): #求最小路徑和以及一條最小和路徑2 n=len(a)3 dp=[[0]*nforiinrange(n)] #二維動態(tài)規(guī)劃數(shù)組4 pre=[[0]*nforiinrange(n)] #二維路徑數(shù)組5 dp[0][0]=a[0][0]6 foriinrange(1,n): #考慮第0列的邊界7 dp[i][0]=dp[i-1][0]+a[i][0]8 pre[i][0]=09 foriinrange(1,n): #考慮對角線的邊界10 dp[i][i]=a[i][i]+dp[i-1][i-1]11 pre[i][i]=i-1i-1,0i,0pre[i][j]=0i-1,i-1i,ipre[i][i]=i-163/9112 foriinrange(2,n): #兩條達(dá)到路徑的結(jié)點(diǎn)13 forjinrange(1,i):14 ifdp[i-1][j-1]<dp[i-1][j]:15 dp[i][j]=a[i][j]+dp[i-1][j-1]16 pre[i][j]=j-117 else:18 dp[i][j]=a[i][j]+dp[i-1][j]19 pre[i][j]=ji-1,ji-1,j-1i,jpre[i][j]=j-1i-1,ji-1,j-1i,jpre[i][j]=j64/9120 ans,minj=min(dp[n-1]),dp[n-1].index(min(dp[n-1]))

#求出dp[n-1]中最小ans和對應(yīng)列號minj21 print("最小路徑和ans=",ans)22 i=n-123 path=[] #存放一條路徑24 whilei>=0

#從(n-1,minj)反推反向路徑25 path.append(a[i][minj])26 minj=pre[i][minj] #最小路徑在上一行中的列號27 i-=1

#在前一行查找28 path.reverse() #逆置path29 print("一條最小路徑:",path)65/91問題求解—自底向上i+1,j+1i+1,ji,

jJI234726598366/91

設(shè)計二維動態(tài)規(guī)劃數(shù)組dp,其中dp[i][j]表示從底部到達(dá)(i,j)位置的最小路徑和。起始位置(n-1,*),所以初始化為dp[n-1][j]=a[n-1][j]。同樣有如下兩個邊界:對于j=0即第0列的任意位置(i,0),只有垂直方向到達(dá)的一條路徑,此時有dp[i][0]=dp[i+1][0]+a[i][0]。對于i=j即對角線上的任意位置(i,i),只有左斜方向到達(dá)的一條路徑,此時有dp[i][i]=dp[i+1][i+1]+a[i][i]。

234726598367/91其他兩條到達(dá)(i,j)位置的路徑,最小路徑和

dp[i][j]=min(dp[i+1][j+1],dp[i+1][j])+a[i][j]。i+1,j+1i+1,ji,

jJI68/91dp[n-1][j]=a[n-1][j] 初始條件dp[i][0]=dp[i+1][0]+a[i][0] 第0列的邊界情況,0≤i≤n-2dp[i][i]=dp[i+1][i+1]+a[i][i] 對角線的邊界情況,0≤i≤n-2dp[i][j]=min(dp[i+1][j],dp[i+1][j+1])+a[i][j]

i<n-1兩條達(dá)到路徑由于第0行只有一個元素,所以dp[0][0]就是最終的最小路徑和。69/911 defminPathSum2(a): #自底向上求最小路徑和2 n=len(a)3 dp=[[0]*nforiinrange(n)] #二維動態(tài)規(guī)劃數(shù)組4 forjinrange(0,n):5 dp[n-1][j]=a[n-1][j]

#第n-1行6 foriinrange(n-2,-1,-1): #考慮第0列的邊界7 dp[i][0]=dp[i+1][0]+a[i][0]8 foriinrange(n-2,-1,-1): #考慮對角線的邊界9 dp[i][i]=a[i][i]+dp[i+1][i+1]10 foriinrange(n-2,-1,-1): #考慮有兩條達(dá)到的路徑11 forjinrange(0,len(a[i])):12 dp[i][j]=min(dp[i+1][j+1],dp[i+1][j])+a[i][j]13 returndp[0][0]70/91自底向上算法空間優(yōu)化在自底向上算法中階段i(指求第i行的dp)僅僅與階段i+1相關(guān),采用降維滾動數(shù)組方式,將dp由二維數(shù)組改為一維數(shù)組。dp[*][j]dp[j]i+1,j+1i+1,ji,

jJI234726598371/911 defminPathSum3(a): #自底向上的優(yōu)化算法2 n=len(a)3 dp=[0]*n #一維動態(tài)規(guī)劃數(shù)組4 foriinrange(n-1,-1,-1):5 forjinrange(0,len(a[i])):6 ifj<len(a)-1:7 dp[j]=min(dp[j],dp[j+1])+a[i][j]8 else:9 dp[j]+=a[i][j]10 returndp[0]10 foriinrange(n-2,-1,-1): #考慮有兩條達(dá)到的路徑11 forjinrange(0,len(a[i])):12 dp[i][j]=min(dp[i+1][j+1],dp[i+1][j])+a[i][j]72/918.3.2實戰(zhàn)—下降路徑最小和(LeetCode931★★)問題描述:給定一個n×n的整數(shù)數(shù)組

matrix,找出并返回通過matrix的下降路徑的最小和。下降路徑可以從第一行中的任何元素開始,并從每一行中選擇一個元素。在下一行選擇的元素和當(dāng)前行所選元素最多相隔一列(即位于正下方或者沿對角線向左或者向右的第一個元素)。具體來說,位置(i,j)的下一個元素應(yīng)當(dāng)是(i+1,j-1)、(i+1,j)或者(i+1,j+1)。

例如,matrix={{2,1,3},{6,5,4},{7,8,9}},答案是13。231645798(a)路徑1231645798(b)路徑273/91解問題求解—自上而下dp[i][j]表示從第0行開始并且以(i,j)位置為終點(diǎn)的下降路徑中最小路徑和。采用自上而下的方式求dp。到達(dá)(i,j)位置的路徑有3條。i-1,ji,ji-1,j-1i-1,j+1min74/91第0行:dp[0][j]=matrix[0][j]。一般情況:

dp[i][j]=min3(dp[i-1][j-1],dp[i-1][j],

dp[i-1][j+1])+matrix[i][j]231645798i-1,ji,ji-1,j-1i-1,j+1min75/91考慮邊界情況如下:①當(dāng)j=0時有:

dp[i][j]=min(dp[i-1][j],dp[i-1][j+1])+matrix[i][j]。②當(dāng)j=n-1時有:

dp[i][j]=min(dp[i-1][j-1],dp[i-1][j])+matrix[i][j]。上述各式合起來構(gòu)成狀態(tài)轉(zhuǎn)移方程,由其求出dp數(shù)組,那么dp中第n-1

行的最小值就是第0行開始到第n-1行的下降路徑中最小路徑和。76/911 class

Solution:2

def

minFallingPathSum(self,

matrix)

->

int:3

n=len(matrix)4

if

n==1:return

matrix[0][0]

#n=1為特殊情況5

dp=[[0]*n

for

i

in

range(n)]

#二維動態(tài)規(guī)劃數(shù)組6

for

j

in

range(0,n):

#第0行:邊界情況7

dp[0][j]=matrix[0][j]77/918

for

i

in

range(1,n):9

for

j

in

range(0,n):10

if

j==0:dp[i][j]=min(dp[i-1][j], dp[i-1][j+1])+matrix[i][j]11

elif

j==n-1:dp[i][j]=min(dp[i-1][j-1], dp[i-1][j])+matrix[i][j]12

else:dp[i][j]=min(dp[i-1][j-1], min(dp[i-1][j],dp[i-1][j+1]))+matrix[i][j]13

ans=min(dp[n-1])

#求dp第n-1行中的最小元素ans14

return

ans78/91自上而下算法空間優(yōu)化由于dp[i]僅僅與dp[i-1]相關(guān),采用滾動數(shù)組方法,將dp數(shù)組大小改為dp[2][n]:用dp[0][j]存放dp[i-1][j],用dp[1][j]存放dp[i][j]。通過變量c實現(xiàn)dp[0]和dp[1]之間的切換。79/911 class

Solution:2

def

minFallingPathSum(self,matrix)

->

int:3

n=len(matrix)4

if

n==1:return

matrix[0][0]

#n=1為特殊情況5

dp=[[0]*n

for

i

in

range(2)]

#二維動態(tài)規(guī)劃數(shù)組80/916

for

j

in

range(0,n):

#第0行:邊界情況7

dp[0][j]=matrix[0][j]8

c=09

for

i

in

range(1,n):10

c=1-c11

for

j

in

range(0,n):12

if

j==0:dp[c][j]=min(dp[1-c][j], dp[1-c][j+1])+matrix[i][j]13

elif

j==n-1:dp[c][j]=min(dp[1-c][j-1], dp[1-c][j])+matrix[i][j]14

else:dp[c][j]=min(dp[1-c][j-1], min(dp[1-c][j],dp[1-c][j+1]))+matrix[i][j]15

ans=min(dp[c])

#求dp第c行中的最小元素ans16

return

ans81/91上述代碼提交時通過,執(zhí)行用時為56ms,內(nèi)存消耗為15.8MB。自下而上的動態(tài)規(guī)劃算法自學(xué)82/918.4三維動態(tài)規(guī)劃三維動態(tài)規(guī)劃是指設(shè)計動態(tài)規(guī)劃算法中采用三維動態(tài)規(guī)劃數(shù)組。說明83/91設(shè)計二維數(shù)組B存放當(dāng)前頂點(diǎn)之間的最短路徑長度,其中B[i][j]表示當(dāng)前頂點(diǎn)i到j(luò)的最短路徑長度。依頂點(diǎn)編號順序處理每個頂點(diǎn),每個頂點(diǎn)的處理看作一個階段,階段k(0≤k≤n-1)的結(jié)果存放在Bk中,Bk[i][j]表示處理完0~k的頂點(diǎn)后得到的頂點(diǎn)i到j(luò)的最短路徑長度。求一個帶權(quán)圖中任意兩個頂點(diǎn)之間的最短路徑長度。8.4.1Floyd算法84/91階段k的處理過程:Bk-1[i][k]Bk-1[k][j]ijkBk-1[i][j]路徑①路徑②B-1[i][j]=A[i][j]Bk[i][j]=min0≤k≤n-1{Bk-1[i][j],Bk-1[i][k]+Bk-1[k][j]}85/911 defFloyd1(A): #Floyd算法3 n=len(A)4 dp=[[[INF]*nforiinrange(n)]forjinrange(n+1)]5 foriinrange(0,n): #求B(-1)6 forjinrange(0,n):7 dp[0][i][j]=A[i][j]8 forkinrange(1,n+1): #依次求B(0)到B(n-1)9 foriinrange(0,n):10 forjinrange(0,n):11 dp[k][i][j]=min(dp[k-1][i][j], dp[k-1][i][k-1]+dp[k-1][k-1][j])B-1[i][j]=A[i][j]Bk[i][j]=min0≤k≤n-1{Bk-1[i][j],Bk-1[i][k]+Bk-1[k][j]}86/91階段k僅僅與階段k-1相關(guān),因此可以將dp滾動為dp[2][MAXN][MAXN]。階段k中求dp[k][i][j]時i和j的任意順序不影響最后的結(jié)果,進(jìn)一步將dp[2][MAXN][MAXN]滾動為二維數(shù)組dp[MAXN][MAXN]??臻g優(yōu)化87/91從狀態(tài)轉(zhuǎn)移方程可以推出如下關(guān)系(其中dp[*][k][k]=0):dp[k][i][k]=min{dp[k-1][i][k],dp[k-1][i][k]+dp[k-1][k][k]} =dp[k-1][i][k]dp[k][k][j]=min{dp[k-1][k][j],dp[k-1][k][k]+dp[k-1][k][j]}=dp[k-1][k][j]在迭代為k時dp[k][i][j]僅僅根據(jù)自己的值以及dp[k-1][i][k]和dp[k-1][k][j]的值計算得出。而dp[k-1][i][k]和dp[k-1][k][j]在階段k中不變,所以可以將dp數(shù)組由三維降為二維。原因解釋:dp[k][i][j]=min0≤k≤n-1{dp[k-1][i][j],dp[k-1][i][k]+dp[k-1][k][j]}88/911 defFloyd(A):2 globaldp3 n=len(A)4 dp=[[INF]*nforiinrange(n)] #二維動態(tài)規(guī)劃數(shù)組5 foriinrange(0,n): #求B(-1)6 forjinrange(0,n):7 dp[i][j]=A[i][j]8 forkinrange(0,n): #依次求B(0)到B(n-1)9 foriinrange(0,n):10 forjinrange(0,n):11 dp[i][j]=min(dp[i][j],dp[i][k]+dp[k][j])89/918.4.2*雙機(jī)調(diào)度問題用兩臺處理機(jī)MA和MB加工n個作業(yè),作業(yè)的編號為0~n-1,兩臺機(jī)器均可以加工任何作業(yè)。第i個作業(yè)單獨(dú)交給MA時的加工時間是a[i],單獨(dú)交給MB時的加工時間是b[i]。現(xiàn)在要求每個作業(yè)只能由一臺機(jī)器加工,但兩臺機(jī)器在任何時刻可以加工兩個不同的作業(yè)。設(shè)計一個動態(tài)規(guī)劃算法,使得兩臺機(jī)器加工完所有n個作業(yè)的最短時間(從任何一臺機(jī)器開工到最后一臺機(jī)器停工的總時間)。例如,n=6,a

為(2,5,7,10,5,2),b為(3,8,4,11,3,4),結(jié)果為15。90/91解三維動態(tài)規(guī)劃數(shù)組一維動態(tài)規(guī)劃數(shù)組自學(xué)91/918.1動態(tài)規(guī)劃概述8.2一維動態(tài)規(guī)劃CONTENTS提綱8.3二維動態(tài)規(guī)劃8.4三維動態(tài)規(guī)劃8.5字符串動態(tài)規(guī)劃8.6背包動態(tài)規(guī)劃8.7樹形動態(tài)規(guī)劃8.8區(qū)間動態(tài)規(guī)劃第8章保存子問題解—動態(tài)規(guī)劃92/878.5字符串動態(tài)規(guī)劃字符串動態(tài)規(guī)劃是指采用動態(tài)規(guī)劃算法求解字符串的相關(guān)問題。說明93/87一個字符串的子序列是指從該字符串中隨意地(不一定連續(xù))去掉若干個字符(可能一個也不去掉)后得到的字符序列。例如"ace"是"abcde"的子序列,但"aec"不是"abcde"的子序列。給定兩個字符串a(chǎn)和b,稱字符串c是a和b的公共子序列,是指c同是a和b的子序列。該問題是求兩個字符串a(chǎn)和b的最長公共子序列(LCS)。8.5.1最長公共子序列問題94/87設(shè)計二維動態(tài)規(guī)劃數(shù)組dp,其中dp[i][j]為(a0,a1,…,ai-1)和(b0,b1,…,bj-1)的最長公共子序列長度,求dp[i][j]的兩種情況。解a0

a1

ai-2

ai-1(a)ai-1=bj-1b0

b1

bj-2

bj-1dp[i-1][j-1]dp[i][j]=dp[i-1][j-1]+1a0

a1

ai-2

ai-1(b)ai-1≠bj-1b0

b1

bj-2

bj-1dp[i][j-1]dp[i][j]=max(dp[i][j-1],dp[i-1][j])a0

a1

ai-2

ai-1b0

b1

bj-2

bj-1dp[i-1][j]max95/87dp[0][0]=0 初始條件dp[i][0]=0 邊界情況dp[0][j]=0 邊界情況dp[i][j]=dp[i-1][j-1]+1 a[i-1]=b[j-1]dp[i][j]=max{dp[i][j-1],dp[i-1][j]} a[i-1]≠b[j-1]狀態(tài)轉(zhuǎn)移方程在求出dp數(shù)組后,dp[m][n]就是a和b的最長公共子序列長度。96/871 defLCSlength(a,b): #求dp2 globaldp3 m,n=len(a),len(b)4 dp=[[0]*(n+1)foriinrange(m+1)]5 dp[0][0]=06 foriinrange(m+1): #將dp[i][0]置為07 dp[i][0]=08 forjinrange(0,n+1): #將dp[0][j]置為09 dp[0][j]=010 foriinrange(1,m+1):11 forjinrange(1,n+1): #循環(huán)處理a、b所有字符12 ifa[i-1]==b[j-1]:

#情況①13 dp[i][j]=dp[i-1][j-1]+114 else: #情況②15 dp[i][j]=max(dp[i][j-1],dp[i-1][j])16 returndp[m][n]【算法分析】算法的時間復(fù)雜度為O(mn),空間復(fù)雜度為O(mn)。97/87

當(dāng)求出dp后如何利用dp求一個最長公共子序列呢?分析狀態(tài)轉(zhuǎn)移方程最后兩行的計算過程可以看出:若dp[i][j]=dp[i-1][j-1]+1,有a[i-1]=b[j-1],也就是說

a[i-1]/b[j-1]是LCS中的字符。若dp[i][j]=dp[i][j-1],有a[i-1]≠b[j-1],也就是說

a[i-1]/b[j-1]不是LCS中的字符。若dp[i][j]=dp[i-1][j],有a[i-1]≠b[j-1],同樣

a[i-1]/b[j-1]不是LCS中的字符。dp[i][j]=dp[i-1][j-1]+1 a[i-1]=b[j-1]dp[i][j]=max{dp[i][j-1],dp[i-1][j]} a[i-1]≠b[j-1]dp

求一個LCS98/87

用一個字符串subs存放一個LCS,i=m,j=n開始向subs中添加k=dp[m][n]個字符,歸納為如下3種情況:若dp[i][j]=dp[i-1][j](即當(dāng)前dp元素值等于上方相鄰元素值),移到上一行即i減1,此時a[i]/b[j]不是LCS字符。若dp[i][j]=dp[i][j-1](即當(dāng)前dp元素值等于左邊相鄰元素值),移到左一列即j減1,此時a[i]/b[j]不是LCS字符。其他情況只能是dp[i][j]=dp[i-1][j-1]+1,移到左上方即i減1同時j減1,此時a[i]/b[j]是LCS的字符,將a[i]/b[j]添加到subs中。i,ji-1,ji-1,j-1i,j-1b[j]a[i]③②①99/87例如,a="abcbdb",m=6,b="acbbabdbb",n=9。

baacbbabdbb0123456789a00000000000b10111111111c20112222222b30122222222d40123333333b5012333344460123444455

若dp[i][j]=dp[i-1][j],移到上一行,此時a[i]/b[j]不是LCS字符。

若dp[i][j]=dp[i][j-1],移到左一列,此時a[i]/b[j]不是LCS字符。若dp[i][j]=dp[i-1][j-1]+1,移到左上方,a[i]/b[j]是LCS的字符。subs="acbdb"100/871 defgetasubs(a,b): #由dp構(gòu)造subs2 subs="" #存放一個LCS3 m,n=len(a),len(b)4 k=dp[m][n] #k為a和b的最長公共子序列長度5 i,j=m,n6 whilek>0: #在subs中放入最長公共子序列(反向)7 ifdp[i][j]==dp[i-1][j]:8 i-=19 elifdp[i][j]==dp[i][j-1]:10 j-=111 else:12 subs+=a[i-1] #subs中添加a[i-1]13 i,j,k=i-1,j-1,k-114 ans=list(subs)15 ans.reverse()16 return"".join(ans) #返回逆置subs的字符串101/87設(shè)a和b是兩個字符串。現(xiàn)在要用最少的字符操作次數(shù)(最優(yōu)編輯距離),將字符串a(chǎn)編輯為字符串b。這里的字符編輯操作共有3種:刪除一個字符,插入一個字符或者將一個字符替換另一個字符。例如,a="sfdqxbw",b="gfdgw",結(jié)果為4。8.5.2編輯距離102

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論