資料結構與演算法學習筆記 動態規劃的入門與程式設計實現

2021-07-16 12:54:16 字數 3958 閱讀 9102

本文的內容部分引自:

我們遇到的問題中,有很大一部分可以用動態規劃(簡稱dp)來解。 解決這類問題可以很大地提公升你的能力與技巧,我會試著幫助你理解如何使用dp來解題。 這篇文章是基於例項展開來講的,因為乾巴巴的理論實在不好理解。

動態規劃演算法通常基於乙個遞推公式及乙個或多個初始狀態當前子問題的解將由上一次子問題的解推出。使用動態規劃來解題只需要多項式時間複雜度, 因此它比回溯法、暴力法等要快許多。

現在讓我們通過乙個例子來了解一下dp的基本原理。

首先,我們要找到某個狀態的最優解,然後在它的幫助下,找到下乙個狀態的最優解

即大問題轉化成小問題,而小問題與大問題同質

如果我們有面值為1元、3元和5元的硬幣若干枚,如何用最少的硬幣湊夠11元? (表面上這道題可以用貪心演算法,但貪心演算法無法保證可以求出解,比如1元換成2元的時候)

好了,讓我們從最小的i開始吧。 當 i

=0,即我們需要多少個硬幣來湊夠0元。 由於1,3,5都大於0,即沒有比0小的幣值,因此湊夠0元我們最少需要0個硬幣。 (這個分析很傻是不是?別著急,這個思路有利於我們理清動態規劃究竟在做些什麼。) 這時候我們發現用乙個標記來表示這句「湊夠0元我們最少需要0個硬幣。」會比較方便, 如果一直用純文本來表述,不出一會兒你就會覺得很繞了。那麼, 我們用d(

i)=j

來表示湊夠i元最少需要j個硬幣。於是我們已經得到了d(

0)=0

, 表示湊夠0元最小需要0個硬幣。 當 i

=1時,只有面值為1元的硬幣可用, 因此我們拿起乙個面值為1的硬幣,接下來只需要湊夠0元即可,而這個是已經知道答案的, 即 d(

0)=0

。所以,

d(1

)=d(

1−1)

+1=d

(0)+

1=0+

1=1

當 i

=2時, 仍然只有面值為1的硬幣可用,於是我拿起乙個面值為1的硬幣, 接下來我只需要再湊夠2-1=1元即可(記得要用最小的硬幣數量),而這個答案也已經知道了。 所以

d(2

)=d(

2−1)

+1=d

(1)+

1=1+

1=2

一直到這裡,你都可能會覺得,好無聊, 感覺像做小學生的題目似的。因為我們一直都只能操作面值為1的硬幣!耐心點, 讓我們看看i=3時的情況。當 i

=3時,我們能用的硬幣就有兩種了:1元的和3元的( 5元的仍然沒用,因為你需要湊的數目是3元!5元太多了親)。 既然能用的硬幣有兩種,我就有兩種方案。如果我拿了乙個1元的硬幣,我的目標就變為了: 湊夠3-1=2元需要的最少硬幣數量。即

d(3

)=d(

3−1)

+1=d

(2)+

1=2+

1=3

這個方案說的是,我拿3個1元的硬幣;第二種方案是我拿起乙個3元的硬幣, 我的目標就變成:湊夠3-3=0元需要的最少硬幣數量。即d(

3)=d

(3−3

)+1=

d(0)

+1=0

+1=1

這個方案說的是,我拿1個3元的硬幣。好了,這兩種方案哪種更優呢? 記得我們可是要用最少的硬幣數量來湊夠3元的。所以, 選擇d(

3)=1

,怎麼來的呢?

具體是這樣得到的:d

(3)=

min

所以得到的狀態轉移方程是:d(

i)=m

in其中 i−

vj>=

0 ,vj

表示第

j 個硬幣的面值;

與數學分析結果的對比:

上面討論了乙個非常簡單的例子。現在讓我們來看看對於更複雜的問題, 如何找到狀態之間的轉移方式(即找到狀態轉移方程)。 為此我們要引入乙個新詞叫遞推關係來將狀態聯絡起來(說的還是狀態轉移方程)

ok,上例子,看看它是如何工作的。

乙個序列有n個數:a[

1],a

[2],

…,a[

n],求出最長非降子串行的長度。

為了方便理解我們是如何找到狀態轉移方程的,我先把下面的例子提到前面來講。 如果我們要求的這n個數的序列是:

5,3,4,8,6,7

根據上面找到的狀態,我們可以得到:(下文的最長非降子串行都用lis表示)

•前1個數的lis長度d(

1)=1

(序列:5)

•前2個數的lis長度d(

2)=1

(序列:3;3前面沒有比3小的)

•前3個數的lis長度d(

3)=2

(序列:3,4;4前面有個比它小的3,所以d(3)=d(2)+1)

•前4個數的lis長度d(

4)=3

(序列:3,4,8;8前面比它小的有3個數,所以 d(

4)=m

ax+1

=3

ok,分析到這,我覺得狀態轉移方程已經很明顯了,如果我們已經求出了d(1)到d(i-1), 那麼d(i)可以用下面的狀態轉移方程得到:d(

i)=m

ax,其

中ja[j]

<=a[

i]用大白話解釋就是,想要求d(i),就把i前面的各個子串行中, 最後乙個數不大於a[i]的序列長度加1,然後取出最大的長度即為d(i)。 當然了,有可能i前面的各個子串行中最後乙個數都大於a[i],那麼d(i)=1, 即它自身成為乙個長度為1的子串行。

與數學分析結果的對比:

動態規劃 資料結構與演算法學習

求最值窮舉 重疊子問題 暴力窮舉效率低下 最優子結構 通過子問題最值得到原問題最值 狀態轉移方程 正確的窮舉 明確 狀態 定義dp陣列 函式的意義 明確 選擇 明確base case fibnacci數列 遞迴 intfib int n 備忘錄解法,自頂向下 intfib int n inthelp...

資料結構與演算法學習筆記

演算法基礎篇 第一章 演算法概述 首先了解一下基本的概念 1.1 什麼是演算法呢?從字面意義上理解,演算法就是用於計算的方法,用這種方法達到預期的結果。通俗的講,演算法可以理解為乙個完整的解題步驟,由一些基本的運算和規定的運算順序組成。通過這樣的解題步驟可以解決特定的問題。演算法可以抽象出5個特徵 ...

演算法學習筆記 動態規劃

在現實生活中,有一類活動的過程,由於它的特殊性,可將過程分成若干個互相聯絡的階段,在它的每一階段都需要作出決策,從而使整個過程達到最好的活動效果。因此各個階段決策的選取不能任意確定,它依賴於當前面臨的狀態,又影響以後的發展。當各個階段決策確定後,就組成乙個決策序列,因而也就確定了整個過程的一條活動路...