日本黄色一级经典视频|伊人久久精品视频|亚洲黄色色周成人视频九九九|av免费网址黄色小短片|黄色Av无码亚洲成年人|亚洲1区2区3区无码|真人黄片免费观看|无码一级小说欧美日免费三级|日韩中文字幕91在线看|精品久久久无码中文字幕边打电话

當(dāng)前位置:首頁(yè) > > 程序員小灰
[導(dǎo)讀]本文通過(guò)斐波那契數(shù)列問(wèn)題和湊零錢(qián)問(wèn)題來(lái)詳解動(dòng)態(tài)規(guī)劃的基本原理。

動(dòng)態(tài)規(guī)劃問(wèn)題的一般形式就是求最值 動(dòng)態(tài)規(guī)劃其實(shí)是運(yùn)籌學(xué)的一種最優(yōu)化方法,只不過(guò)在計(jì)算機(jī)問(wèn)題上應(yīng)用比較多,比如說(shuō)讓你求 最長(zhǎng) 遞增子序列呀, 最小 編輯距離呀等等。

既然是要求最值,核心問(wèn)題是什么呢?求解動(dòng)態(tài)規(guī)劃的核心問(wèn)題是窮舉。因?yàn)橐笞钪?,肯定要把所有可行的答案窮舉出來(lái),然后在其中找最值唄。

動(dòng)態(tài)規(guī)劃就這么簡(jiǎn)單,就是窮舉就完事了?我看到的動(dòng)態(tài)規(guī)劃問(wèn)題都很難??!

首先,動(dòng)態(tài)規(guī)劃的窮舉有點(diǎn)特別,因?yàn)檫@類(lèi)問(wèn)題存在「重疊子問(wèn)題」,如果暴力窮舉的話效率會(huì)極其低下,所以需要「?jìng)渫洝够蛘摺窪P table」來(lái)優(yōu)化窮舉過(guò)程,避免不必要的計(jì)算。

而且,動(dòng)態(tài)規(guī)劃問(wèn)題一定會(huì)具備「最優(yōu)子結(jié)構(gòu)」,才能通過(guò)子問(wèn)題的最值得到原問(wèn)題的最值。

另外,雖然動(dòng)態(tài)規(guī)劃的核心思想就是窮舉求最值,但是問(wèn)題可以千變?nèi)f化,窮舉所有可行解其實(shí)并不是一件容易的事,只有列出正確的「狀態(tài)轉(zhuǎn)移方程才能正確地窮舉。

以上提到的重疊子問(wèn)題、最優(yōu)子結(jié)構(gòu)、狀態(tài)轉(zhuǎn)移方程就是動(dòng)態(tài)規(guī)劃三要素。具體什么意思等會(huì)會(huì)舉例詳解,但是在實(shí)際的算法問(wèn)題中,寫(xiě)出狀態(tài)轉(zhuǎn)移方程是最困難的,這也就是為什么很多朋友覺(jué)得動(dòng)態(tài)規(guī)劃問(wèn)題困難的原因,我來(lái)提供我研究出來(lái)的一個(gè)思維框架,輔助你思考狀態(tài)轉(zhuǎn)移方程:

明確「狀態(tài)」 -> 定義 dp 數(shù)組/函數(shù)的含義 -> 明確「選擇」-> 明確 base case。

下面通過(guò)斐波那契數(shù)列問(wèn)題和湊零錢(qián)問(wèn)題來(lái)詳解動(dòng)態(tài)規(guī)劃的基本原理。前者主要是讓你明白什么是重疊子問(wèn)題(斐波那契數(shù)列嚴(yán)格來(lái)說(shuō)不是動(dòng)態(tài)規(guī)劃問(wèn)題),后者主要集中于如何列出狀態(tài)轉(zhuǎn)移方程。

請(qǐng)讀者不要嫌棄這個(gè)例子簡(jiǎn)單,只有簡(jiǎn)單的例子才能讓你把精力充分集中在算法背后的通用思想和技巧上,而不會(huì)被那些隱晦的細(xì)節(jié)問(wèn)題搞的莫名其妙。想要困難的例子,歷史文章里有的是。

一、斐波那契數(shù)列

1、暴力遞歸

斐波那契數(shù)列的數(shù)學(xué)形式就是遞歸的,寫(xiě)成代碼就是這樣:

int?fib(int?N)?{
????if?(N?==?1?||?N?==?2)?return?1;
????return?fib(N?-?1)?+?fib(N?-?2);
}

這個(gè)不用多說(shuō)了,學(xué)校老師講遞歸的時(shí)候似乎都是拿這個(gè)舉例。我們也知道這樣寫(xiě)代碼雖然簡(jiǎn)潔易懂,但是十分低效,低效在哪里?假設(shè) n = 20,請(qǐng)畫(huà)出遞歸樹(shù)。

PS:但凡遇到需要遞歸的問(wèn)題,最好都畫(huà)出遞歸樹(shù),這對(duì)你分析算法的復(fù)雜度,尋找算法低效的原因都有巨大幫助。

這個(gè)遞歸樹(shù)怎么理解?就是說(shuō)想要計(jì)算原問(wèn)題f(20),我就得先計(jì)算出子問(wèn)題f(19)f(18),然后要計(jì)算f(19),我就要先算出子問(wèn)題f(18)f(17),以此類(lèi)推。最后遇到f(1)或者f(2)的時(shí)候,結(jié)果已知,就能直接返回結(jié)果,遞歸樹(shù)不再向下生長(zhǎng)了。

遞歸算法的時(shí)間復(fù)雜度怎么計(jì)算?子問(wèn)題個(gè)數(shù)乘以解決一個(gè)子問(wèn)題需要的時(shí)間。

子問(wèn)題個(gè)數(shù),即遞歸樹(shù)中節(jié)點(diǎn)的總數(shù)。顯然二叉樹(shù)節(jié)點(diǎn)總數(shù)為指數(shù)級(jí)別,所以子問(wèn)題個(gè)數(shù)為 O(2^n)。

解決一個(gè)子問(wèn)題的時(shí)間,在本算法中,沒(méi)有循環(huán),只有 f(n - 1) + f(n - 2) 一個(gè)加法操作,時(shí)間為 O(1)。

所以,這個(gè)算法的時(shí)間復(fù)雜度為 O(2^n),指數(shù)級(jí)別,爆炸。

觀察遞歸樹(shù),很明顯發(fā)現(xiàn)了算法低效的原因:存在大量重復(fù)計(jì)算,比如f(18)被計(jì)算了兩次,而且你可以看到,以f(18)為根的這個(gè)遞歸樹(shù)體量巨大,多算一遍,會(huì)耗費(fèi)巨大的時(shí)間。更何況,還不止f(18)這一個(gè)節(jié)點(diǎn)被重復(fù)計(jì)算,所以這個(gè)算法及其低效。

這就是動(dòng)態(tài)規(guī)劃問(wèn)題的第一個(gè)性質(zhì):重疊子問(wèn)題。下面,我們想辦法解決這個(gè)問(wèn)題。

2、帶備忘錄的遞歸解法

明確了問(wèn)題,其實(shí)就已經(jīng)把問(wèn)題解決了一半。即然耗時(shí)的原因是重復(fù)計(jì)算,那么我們可以造一個(gè)「?jìng)渫洝?,每次算出某個(gè)子問(wèn)題的答案后別急著返回,先記到「?jìng)渫洝估镌俜祷?;每次遇到一個(gè)子問(wèn)題先去「?jìng)渫洝估锊橐徊?,如果發(fā)現(xiàn)之前已經(jīng)解決過(guò)這個(gè)問(wèn)題了,直接把答案拿出來(lái)用,不要再耗時(shí)去計(jì)算了。

一般使用一個(gè)數(shù)組充當(dāng)這個(gè)「?jìng)渫洝?,?dāng)然你也可以使用哈希表(字典),思想都是一樣的。

int?fib(int?N)?{
????if?(N?1)?return?0;
????//?備忘錄全初始化為?0
????vector<int>?memo(N?+?1,?0);
????//?初始化最簡(jiǎn)情況
????return?helper(memo,?N);
}

int?helper(vector<int>&?memo,?int?n)?{
????//?base?case?
????if?(n?==?1?||?n?==?2)?return?1;
????//?已經(jīng)計(jì)算過(guò)
????if?(memo[n]?!=?0)?return?memo[n];
????memo[n]?=?helper(memo,?n?-?1)?+?
????????????????helper(memo,?n?-?2);
????return?memo[n];
}

現(xiàn)在,畫(huà)出遞歸樹(shù),你就知道「?jìng)渫洝沟降鬃隽耸裁矗?/p>

實(shí)際上,帶「?jìng)渫洝沟倪f歸算法,把一棵存在巨量冗余的遞歸樹(shù)通過(guò)「剪枝」,改造成了一幅不存在冗余的遞歸圖,極大減少了子問(wèn)題(即遞歸圖中節(jié)點(diǎn))的個(gè)數(shù)。

遞歸算法的時(shí)間復(fù)雜度怎么算?子問(wèn)題個(gè)數(shù)乘以解決一個(gè)子問(wèn)題需要的時(shí)間。

子問(wèn)題個(gè)數(shù),即圖中節(jié)點(diǎn)的總數(shù),由于本算法不存在冗余計(jì)算,子問(wèn)題就是f(1),f(2),f(3)f(20),數(shù)量和輸入規(guī)模 n = 20 成正比,所以子問(wèn)題個(gè)數(shù)為 O(n)。

解決一個(gè)子問(wèn)題的時(shí)間,同上,沒(méi)有什么循環(huán),時(shí)間為 O(1)。

所以,本算法的時(shí)間復(fù)雜度是 O(n)。比起暴力算法,是降維打擊。

至此,帶備忘錄的遞歸解法的效率已經(jīng)和迭代的動(dòng)態(tài)規(guī)劃一樣了。實(shí)際上,這種解法和迭代的動(dòng)態(tài)規(guī)劃思想已經(jīng)差不多,只不過(guò)這種方法叫做「自頂向下」,動(dòng)態(tài)規(guī)劃叫做「自底向上」。

啥叫「自頂向下」?注意我們剛才畫(huà)的遞歸樹(shù)(或者說(shuō)圖),是從上向下延伸,都是從一個(gè)規(guī)模較大的原問(wèn)題比如說(shuō)f(20),向下逐漸分解規(guī)模,直到f(1)f(2)觸底,然后逐層返回答案,這就叫「自頂向下」。

啥叫「自底向上」?反過(guò)來(lái),我們直接從最底下,最簡(jiǎn)單,問(wèn)題規(guī)模最小的f(1)f(2)開(kāi)始往上推,直到推到我們想要的答案f(20),這就是動(dòng)態(tài)規(guī)劃的思路,這也是為什么動(dòng)態(tài)規(guī)劃一般都脫離了遞歸,而是由循環(huán)迭代完成計(jì)算。

3、dp 數(shù)組的迭代解法

有了上一步「?jìng)渫洝沟膯l(fā),我們可以把這個(gè)「?jìng)渫洝躬?dú)立出來(lái)成為一張表,就叫做 DP table 吧,在這張表上完成「自底向上」的推算豈不美哉!

int?fib(int?N)?{
????vector<int>?dp(N?+?1,?0);
????//?base?case
????dp[1]?=?dp[2]?=?1;
????for?(int?i?=?3;?i?<=?N;?i++)
????????dp[i]?=?dp[i?-?1]?+?dp[i?-?2];
????return?dp[N];
}

畫(huà)個(gè)圖就很好理解了,而且你發(fā)現(xiàn)這個(gè) DP table 特別像之前那個(gè)「剪枝」后的結(jié)果,只是反過(guò)來(lái)算而已。實(shí)際上,帶備忘錄的遞歸解法中的「?jìng)渫洝?,最終完成后就是這個(gè) DP table,所以說(shuō)這兩種解法其實(shí)是差不多的,大部分情況下,效率也基本相同。

這里,引出「狀態(tài)轉(zhuǎn)移方程」這個(gè)名詞,實(shí)際上就是描述問(wèn)題結(jié)構(gòu)的數(shù)學(xué)形式:

為啥叫「狀態(tài)轉(zhuǎn)移方程」?為了聽(tīng)起來(lái)高端。你把 f(n) 想做一個(gè)狀態(tài) n,這個(gè)狀態(tài) n 是由狀態(tài) n - 1 和狀態(tài) n - 2 相加轉(zhuǎn)移而來(lái),這就叫狀態(tài)轉(zhuǎn)移,僅此而已。

你會(huì)發(fā)現(xiàn),上面的幾種解法中的所有操作,例如 return f(n - 1) + f(n - 2),dp[i] = dp[i - 1] + dp[i - 2],以及對(duì)備忘錄或 DP table 的初始化操作,都是圍繞這個(gè)方程式的不同表現(xiàn)形式??梢?jiàn)列出「狀態(tài)轉(zhuǎn)移方程」的重要性,它是解決問(wèn)題的核心。很容易發(fā)現(xiàn),其實(shí)狀態(tài)轉(zhuǎn)移方程直接代表著暴力解法。

千萬(wàn)不要看不起暴力解,動(dòng)態(tài)規(guī)劃問(wèn)題最困難的就是寫(xiě)出狀態(tài)轉(zhuǎn)移方程,即這個(gè)暴力解。優(yōu)化方法無(wú)非是用備忘錄或者 DP table,再無(wú)奧妙可言。

這個(gè)例子的最后,講一個(gè)細(xì)節(jié)優(yōu)化。細(xì)心的讀者會(huì)發(fā)現(xiàn),根據(jù)斐波那契數(shù)列的狀態(tài)轉(zhuǎn)移方程,當(dāng)前狀態(tài)只和之前的兩個(gè)狀態(tài)有關(guān),其實(shí)并不需要那么長(zhǎng)的一個(gè) DP table 來(lái)存儲(chǔ)所有的狀態(tài),只要想辦法存儲(chǔ)之前的兩個(gè)狀態(tài)就行了。所以,可以進(jìn)一步優(yōu)化,把空間復(fù)雜度降為 O(1):

int?fib(int?n)?{
????if?(n?==?2?||?n?==?1)?
????????return?1;
????int?prev?=?1,?curr?=?1;
????for?(int?i?=?3;?i?<=?n;?i++)?{
????????int?sum?=?prev?+?curr;
????????prev?=?curr;
????????curr?=?sum;
????}
????return?curr;
}

有人會(huì)問(wèn),動(dòng)態(tài)規(guī)劃的另一個(gè)重要特性「最優(yōu)子結(jié)構(gòu)」,怎么沒(méi)有涉及?下面會(huì)涉及。斐波那契數(shù)列的例子嚴(yán)格來(lái)說(shuō)不算動(dòng)態(tài)規(guī)劃,因?yàn)闆](méi)有涉及求最值,以上旨在演示算法設(shè)計(jì)螺旋上升的過(guò)程。

下面,看第二個(gè)例子,湊零錢(qián)問(wèn)題。

二、湊零錢(qián)問(wèn)題

先看下題目:給你k種面值的硬幣,面值分別為c1, c2 ... ck,每種硬幣的數(shù)量無(wú)限,再給一個(gè)總金額amount,問(wèn)你最少需要幾枚硬幣湊出這個(gè)金額,如果不可能湊出,算法返回 -1 。算法的函數(shù)簽名如下:

// coins 中是可選硬幣面值,amount 是目標(biāo)金額
int?coinChange(int[]?coins,?int?amount);

比如說(shuō)k = 3,面值分別為 1,2,5,總金額amount = 11。那么最少需要 3 枚硬幣湊出,即 11 = 5 + 5 + 1。

你認(rèn)為計(jì)算機(jī)應(yīng)該如何解決這個(gè)問(wèn)題?顯然,就是把所有肯能的湊硬幣方法都窮舉出來(lái),然后找找看最少需要多少枚硬幣。

1、暴力遞歸

首先,這個(gè)問(wèn)題是動(dòng)態(tài)規(guī)劃問(wèn)題,因?yàn)樗哂小缸顑?yōu)子結(jié)構(gòu)」。要符合「最優(yōu)子結(jié)構(gòu)」,子問(wèn)題間必須互相獨(dú)立。啥叫相互獨(dú)立?你肯定不想看數(shù)學(xué)證明,我用一個(gè)直觀的例子來(lái)講解。

比如說(shuō),你的原問(wèn)題是考出最高的總成績(jī),那么你的子問(wèn)題就是要把語(yǔ)文考到最高,數(shù)學(xué)考到最高…… 為了每門(mén)課考到最高,你要把每門(mén)課相應(yīng)的選擇題分?jǐn)?shù)拿到最高,填空題分?jǐn)?shù)拿到最高…… 當(dāng)然,最終就是你每門(mén)課都是滿分,這就是最高的總成績(jī)。

得到了正確的結(jié)果:最高的總成績(jī)就是總分。因?yàn)檫@個(gè)過(guò)程符合最優(yōu)子結(jié)構(gòu),“每門(mén)科目考到最高”這些子問(wèn)題是互相獨(dú)立,互不干擾的。

但是,如果加一個(gè)條件:你的語(yǔ)文成績(jī)和數(shù)學(xué)成績(jī)會(huì)互相制約,此消彼長(zhǎng)。這樣的話,顯然你能考到的最高總成績(jī)就達(dá)不到總分了,按剛才那個(gè)思路就會(huì)得到錯(cuò)誤的結(jié)果。因?yàn)樽訂?wèn)題并不獨(dú)立,語(yǔ)文數(shù)學(xué)成績(jī)無(wú)法同時(shí)最優(yōu),所以最優(yōu)子結(jié)構(gòu)被破壞。

回到湊零錢(qián)問(wèn)題,為什么說(shuō)它符合最優(yōu)子結(jié)構(gòu)呢?比如你想求amount = 11時(shí)的最少硬幣數(shù)(原問(wèn)題),如果你知道湊出amount = 10的最少硬幣數(shù)(子問(wèn)題),你只需要把子問(wèn)題的答案加一(再選一枚面值為 1 的硬幣)就是原問(wèn)題的答案,因?yàn)橛矌诺臄?shù)量是沒(méi)有限制的,子問(wèn)題之間沒(méi)有相互制,是互相獨(dú)立的。

那么,既然知道了這是個(gè)動(dòng)態(tài)規(guī)劃問(wèn)題,就要思考如何列出正確的狀態(tài)轉(zhuǎn)移方程。

先確定「狀態(tài)」,也就是原問(wèn)題和子問(wèn)題中變化的變量。由于硬幣數(shù)量無(wú)限,所以唯一的狀態(tài)就是目標(biāo)金額amount。

然后確定dp函數(shù)的定義:函數(shù)?dp(n)表示,當(dāng)前的目標(biāo)金額是n,至少需要dp(n)個(gè)硬幣湊出該金額。

然后確定「選擇」并擇優(yōu),也就是對(duì)于每個(gè)狀態(tài),可以做出什么選擇改變當(dāng)前狀態(tài)。具體到這個(gè)問(wèn)題,無(wú)論當(dāng)?shù)哪繕?biāo)金額是多少,選擇就是從面額列表coins中選擇一個(gè)硬幣,然后目標(biāo)金額就會(huì)減少:

#?偽碼框架
def?coinChange(coins:?List[int],?amount:?int):
????#?定義:要湊出金額 n,至少要 dp(n)?個(gè)硬幣
????def?dp(n):
????????#?做選擇,需要硬幣最少的那個(gè)結(jié)果就是答案
????????for?coin?in?coins:
????????????res?=?min(res,?1?+?dp(n?-?coin))
????????return?res
????#?我們要求目標(biāo)金額是 amount
????return?dp(amount)

最后明確 base case,顯然目標(biāo)金額為 0 時(shí),所需硬幣數(shù)量為 0;當(dāng)目標(biāo)金額小于 0 時(shí),無(wú)解,返回 -1:

def?coinChange(coins:?List[int],?amount:?int):

????def?dp(n):
????????#?base?case
????????if?n?==?0:?return?0
????????if?n?0:?return?-1
????????#?求最小值,所以初始化為正無(wú)窮
????????res?=?float('INF')
????????for?coin?in?coins:
????????????subproblem?=?dp(n?-?coin)
????????????#?子問(wèn)題無(wú)解,跳過(guò)
????????????if?subproblem?==?-1:?continue
????????????res?=?min(res,?1?+?subproblem)

????????return?res?if?res?!=?float('INF')?else?-1

????return?dp(amount)

至此,狀態(tài)轉(zhuǎn)移方程其實(shí)已經(jīng)完成了,以上算法已經(jīng)是暴力解法了,以上代碼的數(shù)學(xué)形式就是狀態(tài)轉(zhuǎn)移方程:

至此,這個(gè)問(wèn)題其實(shí)就解決了,只不過(guò)需要消除一下重疊子問(wèn)題,比如amount = 11, coins = {1,2,5}時(shí)畫(huà)出遞歸樹(shù)看看:

時(shí)間復(fù)雜度分析:子問(wèn)題總數(shù) x 解決每個(gè)子問(wèn)題的時(shí)間。

子問(wèn)題總數(shù)為遞歸樹(shù)節(jié)點(diǎn)個(gè)數(shù),這個(gè)比較難看出來(lái),是 O(n^k),總之是指數(shù)級(jí)別的。每個(gè)子問(wèn)題中含有一個(gè) for 循環(huán),復(fù)雜度為 O(k)。所以總時(shí)間復(fù)雜度為 O(k * n^k),指數(shù)級(jí)別。

2、帶備忘錄的遞歸

只需要稍加修改,就可以通過(guò)備忘錄消除子問(wèn)題:

def?coinChange(coins:?List[int],?amount:?int):
????#?備忘錄
????memo?=?dict()
????def?dp(n):
????????#?查備忘錄,避免重復(fù)計(jì)算
????????if?n?in?memo:?return?memo[n]

????????if?n?==?0:?return?0
????????if?n?0:?return?-1
????????res?=?float('INF')
????????for?coin?in?coins:
????????????subproblem?=?dp(n?-?coin)
????????????if?subproblem?==?-1:?continue
????????????res?=?min(res,?1?+?subproblem)

????????#?記入備忘錄
????????memo[n]?=?res?if?res?!=?float('INF')?else?-1
????????return?memo[n]

????return?dp(amount)

不畫(huà)圖了,很顯然「?jìng)渫洝勾蟠鬁p小了子問(wèn)題數(shù)目,完全消除了子問(wèn)題的冗余,所以子問(wèn)題總數(shù)不會(huì)超過(guò)金額數(shù) n,即子問(wèn)題數(shù)目為 O(n)。處理一個(gè)子問(wèn)題的時(shí)間不變,仍是 O(k),所以總的時(shí)間復(fù)雜度是 O(kn)。

3、dp 數(shù)組的迭代解法

當(dāng)然,我們也可以自底向上使用 dp table 來(lái)消除重疊子問(wèn)題,dp數(shù)組的定義和剛才dp函數(shù)類(lèi)似,定義也是一樣的:

dp[i] = x表示,當(dāng)目標(biāo)金額為i時(shí),至少需要x枚硬幣。

int?coinChange(vector<int>&?coins,?int?amount)?{
????//?數(shù)組大小為?amount?+?1,初始值也為?amount?+?1
????vector<int>?dp(amount?+?1,?amount?+?1);
????//?base?case
????dp[0]?=?0;
????for?(int?i?=?0;?i?????????//?內(nèi)層?for?在求所有子問(wèn)題?+?1?的最小值
????????for?(int?coin?:?coins)?{
????????????//?子問(wèn)題無(wú)解,跳過(guò)
????????????if?(i?-?coin?0)?continue;
????????????dp[i]?=?min(dp[i],?1?+?dp[i?-?coin]);
????????}
????}
????return?(dp[amount]?==?amount?+?1)???-1?:?dp[amount];
}

PS:為啥dp數(shù)組初始化為amount + 1呢,因?yàn)闇惓?code style="margin-right: 2px;margin-left: 2px;padding: 2px 4px;font-size: inherit;line-height: inherit;border-radius: 4px;color: rgb(233, 105, 0);background: rgb(248, 248, 248);">amount金額的硬幣數(shù)最多只可能等于amount(全用 1 元面值的硬幣),所以初始化為amount + 1就相當(dāng)于初始化為正無(wú)窮,便于后續(xù)取最小值。

三、最后總結(jié)

第一個(gè)斐波那契數(shù)列的問(wèn)題,解釋了如何通過(guò)「?jìng)渫洝够蛘摺竏p table」的方法來(lái)優(yōu)化遞歸樹(shù),并且明確了這兩種方法本質(zhì)上是一樣的,只是自頂向下和自底向上的不同而已。

第二個(gè)湊零錢(qián)的問(wèn)題,展示了如何流程化確定「狀態(tài)轉(zhuǎn)移方程」,只要通過(guò)狀態(tài)轉(zhuǎn)移方程寫(xiě)出暴力遞歸解,剩下的也就是優(yōu)化遞歸樹(shù),消除重疊子問(wèn)題而已。

如果你不太了解動(dòng)態(tài)規(guī)劃,還能看到這里,真得給你鼓掌,相信你已經(jīng)掌握了這個(gè)算法的設(shè)計(jì)技巧。

計(jì)算機(jī)解決問(wèn)題其實(shí)沒(méi)有任何奇技淫巧,它唯一的解決辦法就是窮舉,窮舉所有可能性。算法設(shè)計(jì)無(wú)非就是先思考“如何窮舉”,然后再追求“如何聰明地窮舉”。

列出動(dòng)態(tài)轉(zhuǎn)移方程,就是在解決“如何窮舉”的問(wèn)題。之所以說(shuō)它難,一是因?yàn)楹芏喔F舉需要遞歸實(shí)現(xiàn),二是因?yàn)橛械膯?wèn)題本身的解空間復(fù)雜,不那么容易窮舉完整。

備忘錄、DP table 就是在追求“如何聰明地窮舉”。用空間換時(shí)間的思路,是降低時(shí)間復(fù)雜度的不二法門(mén),除此之外,試問(wèn),還能玩出啥花活?


PS:
這篇文章是小灰的朋友labuladong寫(xiě)的,他的新書(shū)《labuladong的算法小抄》出版了,恭喜他!
? ? ? ? ? ? ? ?
在樣稿階段,我就看過(guò)這本書(shū),而且還專(zhuān)門(mén)寫(xiě)了個(gè)推薦語(yǔ)。
?
這本書(shū)是手把手帶你刷算法題的,全彩色,四百多頁(yè),干貨很多。作者用通俗易懂的語(yǔ)言講清楚了許多經(jīng)典的算法題目,很多題目都是在知名企業(yè)面試時(shí)候經(jīng)常遇到的,吃透之后肯定會(huì)增加你拿大廠offer的幾率。
?
作者自己也是把國(guó)內(nèi)一線大廠的offer拿了個(gè)遍,簡(jiǎn)直是一個(gè)offer收割機(jī)了。算法難題時(shí)時(shí)有,唯有套路得人心,他把多年算法刷題的經(jīng)驗(yàn)給總結(jié)出套路,即學(xué)即用,非常實(shí)在。本書(shū)絕對(duì)可以幫助你掃清算法路上的障礙。當(dāng)當(dāng)這兩天還有優(yōu)惠,需要的趕緊買(mǎi)本看看吧:

免責(zé)聲明:本文內(nèi)容由21ic獲得授權(quán)后發(fā)布,版權(quán)歸原作者所有,本平臺(tái)僅提供信息存儲(chǔ)服務(wù)。文章僅代表作者個(gè)人觀點(diǎn),不代表本平臺(tái)立場(chǎng),如有問(wèn)題,請(qǐng)聯(lián)系我們,謝謝!

本站聲明: 本文章由作者或相關(guān)機(jī)構(gòu)授權(quán)發(fā)布,目的在于傳遞更多信息,并不代表本站贊同其觀點(diǎn),本站亦不保證或承諾內(nèi)容真實(shí)性等。需要轉(zhuǎn)載請(qǐng)聯(lián)系該專(zhuān)欄作者,如若文章內(nèi)容侵犯您的權(quán)益,請(qǐng)及時(shí)聯(lián)系本站刪除。
換一批
延伸閱讀

LED驅(qū)動(dòng)電源的輸入包括高壓工頻交流(即市電)、低壓直流、高壓直流、低壓高頻交流(如電子變壓器的輸出)等。

關(guān)鍵字: 驅(qū)動(dòng)電源

在工業(yè)自動(dòng)化蓬勃發(fā)展的當(dāng)下,工業(yè)電機(jī)作為核心動(dòng)力設(shè)備,其驅(qū)動(dòng)電源的性能直接關(guān)系到整個(gè)系統(tǒng)的穩(wěn)定性和可靠性。其中,反電動(dòng)勢(shì)抑制與過(guò)流保護(hù)是驅(qū)動(dòng)電源設(shè)計(jì)中至關(guān)重要的兩個(gè)環(huán)節(jié),集成化方案的設(shè)計(jì)成為提升電機(jī)驅(qū)動(dòng)性能的關(guān)鍵。

關(guān)鍵字: 工業(yè)電機(jī) 驅(qū)動(dòng)電源

LED 驅(qū)動(dòng)電源作為 LED 照明系統(tǒng)的 “心臟”,其穩(wěn)定性直接決定了整個(gè)照明設(shè)備的使用壽命。然而,在實(shí)際應(yīng)用中,LED 驅(qū)動(dòng)電源易損壞的問(wèn)題卻十分常見(jiàn),不僅增加了維護(hù)成本,還影響了用戶(hù)體驗(yàn)。要解決這一問(wèn)題,需從設(shè)計(jì)、生...

關(guān)鍵字: 驅(qū)動(dòng)電源 照明系統(tǒng) 散熱

根據(jù)LED驅(qū)動(dòng)電源的公式,電感內(nèi)電流波動(dòng)大小和電感值成反比,輸出紋波和輸出電容值成反比。所以加大電感值和輸出電容值可以減小紋波。

關(guān)鍵字: LED 設(shè)計(jì) 驅(qū)動(dòng)電源

電動(dòng)汽車(chē)(EV)作為新能源汽車(chē)的重要代表,正逐漸成為全球汽車(chē)產(chǎn)業(yè)的重要發(fā)展方向。電動(dòng)汽車(chē)的核心技術(shù)之一是電機(jī)驅(qū)動(dòng)控制系統(tǒng),而絕緣柵雙極型晶體管(IGBT)作為電機(jī)驅(qū)動(dòng)系統(tǒng)中的關(guān)鍵元件,其性能直接影響到電動(dòng)汽車(chē)的動(dòng)力性能和...

關(guān)鍵字: 電動(dòng)汽車(chē) 新能源 驅(qū)動(dòng)電源

在現(xiàn)代城市建設(shè)中,街道及停車(chē)場(chǎng)照明作為基礎(chǔ)設(shè)施的重要組成部分,其質(zhì)量和效率直接關(guān)系到城市的公共安全、居民生活質(zhì)量和能源利用效率。隨著科技的進(jìn)步,高亮度白光發(fā)光二極管(LED)因其獨(dú)特的優(yōu)勢(shì)逐漸取代傳統(tǒng)光源,成為大功率區(qū)域...

關(guān)鍵字: 發(fā)光二極管 驅(qū)動(dòng)電源 LED

LED通用照明設(shè)計(jì)工程師會(huì)遇到許多挑戰(zhàn),如功率密度、功率因數(shù)校正(PFC)、空間受限和可靠性等。

關(guān)鍵字: LED 驅(qū)動(dòng)電源 功率因數(shù)校正

在LED照明技術(shù)日益普及的今天,LED驅(qū)動(dòng)電源的電磁干擾(EMI)問(wèn)題成為了一個(gè)不可忽視的挑戰(zhàn)。電磁干擾不僅會(huì)影響LED燈具的正常工作,還可能對(duì)周?chē)娮釉O(shè)備造成不利影響,甚至引發(fā)系統(tǒng)故障。因此,采取有效的硬件措施來(lái)解決L...

關(guān)鍵字: LED照明技術(shù) 電磁干擾 驅(qū)動(dòng)電源

開(kāi)關(guān)電源具有效率高的特性,而且開(kāi)關(guān)電源的變壓器體積比串聯(lián)穩(wěn)壓型電源的要小得多,電源電路比較整潔,整機(jī)重量也有所下降,所以,現(xiàn)在的LED驅(qū)動(dòng)電源

關(guān)鍵字: LED 驅(qū)動(dòng)電源 開(kāi)關(guān)電源

LED驅(qū)動(dòng)電源是把電源供應(yīng)轉(zhuǎn)換為特定的電壓電流以驅(qū)動(dòng)LED發(fā)光的電壓轉(zhuǎn)換器,通常情況下:LED驅(qū)動(dòng)電源的輸入包括高壓工頻交流(即市電)、低壓直流、高壓直流、低壓高頻交流(如電子變壓器的輸出)等。

關(guān)鍵字: LED 隧道燈 驅(qū)動(dòng)電源
關(guān)閉