很是特別的一個動態規劃新手教程

很是特別的一個動態規劃新手教程
今天在網上看到一個講動態規劃的文章,是以01揹包爲例的,這文章和書上的解說很不同,令我眼前一亮,因而轉載一下下~~~
(說明一下,本人很痛恨教材公式定理漫天飛,實際的解說卻講得很枯澀難懂,這樣的中國式的教育已經延綿了幾千年了,現在中國的教材仍是這個樣子,講清楚些明確些就那麼難麼?高中有個老師講的一句話一直認爲頗有道理:「教得會天才不是真本事,能把博士生的東西講到小學生都會用那纔是真水平。」)
附上原文地址:
http://www.cnblogs.com/sdjl/articles/1274312.html


經過金礦模型介紹動態規劃          對於動態規劃,每個剛接觸的人都需要一段時間來理解,特別是第一次接觸的時候老是想不通爲何這樣的方法可行,這篇文章就是爲了幫助你們理解動態規劃,並經過解說主要的01揹包問題來引導讀者怎樣去思考動態規劃。本文力求通俗易懂,無異性,不讓讀者感到迷惑,引導讀者去思考,因此假設你在閱讀中發現有不通順的地方,讓你產生錯誤理解的地方,讓你可貴讀懂的地方,請跟貼指出,謝謝!  ----第一節----初識動態規劃--------        經典的01揹包問題是這種:        有一個包和n個物品,包的容量爲m,每個物品都有各自的體積和價值,問當從這n個物品中選擇多個物品放在包裏而物品體積總數不超過包的容量m時,能夠獲得的最大價值是多少?[對於每個物品不能夠取屢次,最多僅僅能取一次,之因此叫作01揹包,0表示不取,1表示取]        爲了用一種生動又更形象的方式來解說此題,我把此題用還有一種方式來描寫敘述,例如如下:               有一個國家,所有的國民都很老實憨厚,某天他們在本身的國家發現了十座金礦,並且這十座金礦在地圖上排成一條直線,國王知道這個消息後很高興,他但願能夠把這些金子都挖出來造福國民,首先他把這些金礦依照在地圖上的位置從西至東進行編號,依次爲0、一、二、三、四、五、六、七、八、9,而後他命令他的手下去對每一座金礦進行勘測,以便知道挖取每一座金礦需要多少人力以及每座金礦能夠挖出多少金子,而後動員國民都來挖金子。        題目補充1:挖每一座金礦需要的人數是固定的,多一我的少一我的都不行。國王知道每個金礦各需要多少人手,金礦i需要的人數爲peopleNeeded 。        題目補充2:每一座金礦所挖出來的金子數是固定的,當第i座金礦有peopleNeeded 人去挖的話,就必定能剛好挖出gold 個金子。不然一個金子都挖不出來。        題目補充3:開採一座金礦的人完畢開採工做後,他們不會再次去開採其餘金礦,所以一我的最多僅僅能使用一次。        題目補充4:國王在全國範圍內僅招募到了10000名願意爲了國家去挖金子的人,所以這些人可能不夠把所有的金子都挖出來,但是國王但願挖到的金子越多越好。        題目補充5:這個國家的每一個人都很是老實(包含國王),不會私吞不論什麼金子,也不會弄虛做假,不會說謊言。        題目補充6:有很是多人拿到這個題後的第一反應就是對每一個金礦求出平均每一個人能挖出多少金子,而後從高到低進行選擇,這裏要強調這樣的方法是錯的,假設你也是這樣想的,請考慮揹包模型,當有一個揹包的容量爲10,共同擁有3個物品,體積各自是三、三、5,價值各自是六、六、9,那麼你的方法取到的是前兩個物品,總價值是12,但明顯最大值是後兩個物品組成的15。        題目補充7:咱們僅僅需要知道最多可以挖出多少金子就能夠,而不用關心哪些金礦挖哪些金礦不挖。        那麼,國王到底怎樣知道在僅僅有10000我的的狀況下最多能挖出多少金子呢?國王是怎樣思考這個問題的呢?        國王首先來到了第9個金礦的所在地(注意,第9個就是最後一個,因爲是從0開始編號的,最西邊的那個金礦是第0個),他的臣子告訴他,假設要挖取第9個金礦的話就需要1500我的,並且第9個金礦可以挖出8888個金子。聽到這裏國王哈哈大笑起來,因爲原先他覺得要知道十個金礦在僅有10000我的的狀況下最多能挖出多少金子是一件很是難思考的問題,但是,就在剛纔聽完他的臣子所說的那句話時,國王已經知道總共最多能挖出多少金子了,國王是怎樣在不瞭解其餘金礦的狀況下知道最多能挖出多少金子的呢?他的臣子們也不知道這個謎,所以他的臣子們就問他了:「最聰明的國王陛下,咱們都沒有告訴您其餘金礦的狀況,您是怎樣知道終於答案的呢?」        得意的國王笑了笑,而後把他最得意的「左、右手」叫到跟前,說到:「我並不需要考慮終於要挖哪些金礦才幹獲得最多的金子,我僅僅需要考慮我面前的這座金礦就可以了,對於我面前的這座金礦不外乎僅有兩種選擇,要麼挖,要麼不挖,對吧?」        「固然,固然」大臣們回答倒。               國王繼續說道:「假設我挖取第9座金礦的話那麼我現在就能得到8888個金子,而我將用去1500我的,那麼我還剩下8500我的。我親愛的左部下,假設你告訴我當我把所有剩下的8500我的和所有剩下的其餘金礦都交給你去開採你最多能給我挖出多少金子的話,那麼我不就知道了在第9個金礦必定開採的狀況下所能獲得的最大金幣數嗎?」               國王的左部下聽後回答道:「國王陛下,您的意思是假設我能用8500我的在其餘金礦最多開採出x個金幣的話,那您一共就行得到 x + 8888個金子,對嗎?」               「是啊,是啊……假設第9座金礦必定開採的話……」大臣們點頭說到。               國王笑着繼續對着他的右部下說到:「親愛的右部下,或許我並不打算開採這第9座金礦,那麼我依舊擁有10000我的,假設我把這10000我的和剩下的金礦都給你的話,你最多能給我挖出多少個金子呢?」               國王的右部下聰明地說道:「尊敬的國王陛下,我明確您的意思了,假設我回答最多能購開採出y個金幣的話,那您就可以在y和x+8888之間選擇一個較大者,而這個較大者就是終於咱們能得到的最大金幣數,您看我這樣理解對嗎?」               國王笑得更燦爛了,問他的左部下:「那麼親愛的左部下,我給你8500我的和其他金礦的話你能告訴我最多能挖出多少金子嗎?」        「請您放心,這個問題難不倒我」。左部下向國王打包票說到。        國王高興地繼續問他的右部下:「那右部下你呢,假設我給你10000我的和其他金礦的話你能告訴我最多能挖出多少金子嗎?」        「固然能了!交給我吧!」右部下同左部下同樣自信地回答道。        「那就拜託給大家兩位了,現在我要回到我那溫馨的王宮裏去享受了,我期待着大家的答覆。」國王說完就開始動身回去等消息了,他是多麼地相信他的兩個大臣能夠給他一個準確的答覆,因爲國王事實上知道他的兩位大臣要比他聰明得多。        故事發展到這裏,你是否在想國王的這兩個大臣又是怎樣找到讓國王愜意的答案的呢?他們爲何能夠如此自信呢?其實他們的確比國王要聰明一些,因爲他們從國王的身上學到了一點,就是這一點讓他們充滿了自信。        國王走後,國王的左、右部下來到了第8座金礦,早已在那裏等待他們的金礦勘測兵向兩位大臣報道:「聰明的兩位大臣,您們好,第8座金礦需要1000我的才幹開採,可以得到7000個金子」。        因爲國王僅給他的左部下8500我的,因此國王的左部下叫來了兩我的,對着當中一我的問到:「假設我給你7500我的和除了第八、第9的其餘所有金礦的話,你能告訴我你最多能挖出多少金子嗎?」        而後國王的左部下繼續問還有一我的:「假設我給你8500我的和除了第八、第9的其餘所有金礦的話,你能告訴我你最多能挖出多少金子嗎?」        國王的左部下在內心想着:「假設他們倆都能回答個人問題的話,那國王交給個人問題不就攻克了嗎?哈哈哈!」        因爲國王給了他的右部下10000我的,因此國王的右部下相同也叫來了兩我的,對着當中一我的問:「假設我給你9000我的和除了第八、第9的其餘所有金礦的話,你能告訴我你最多能挖出多少金子嗎?」        而後國王的右部下繼續問他叫來的還有一我的:「假設我給你10000我的和除了第八、第9的其餘所有金礦的話,你能告訴我你最多能挖出多少金子嗎?」        此時,國王的右部下同左部下同樣,他們都在爲本身如此聰明而感到知足。               固然,這四個被叫來的人相同自信地回答沒有問題,因爲他們相同地從這兩個大臣身上學到了相同的一點,而兩位自以爲本身同樣很是聰明的大臣得意地笑着回到了他們的府邸,等着別人回答他們提出來的問題,現在你知道了這兩個大臣是怎樣解決國王交待給他們的問題了嗎?        那麼你以爲被大臣叫去的那四我的又是怎麼完畢大臣交給他們的問題的呢?答案固然是他們找到了另外八我的!        沒用多少功夫,這個問題已經在全國傳開了,不少其它人的人找到了更不少其它的人來解決問題,而有些人卻不需要去另外找兩我的幫他,哪些人不需要別人的幫助就可以回答他們的問題呢?        很是明顯,當被問到給你z我的和僅有第0座金礦時最多能挖出多少金子時,就不需要別人的幫助,因爲你知道,假設z大於等於挖取第0座金礦所需要的人數的話,那麼挖出來的最多金子數就是第0座金礦能夠挖出來的金子數,假設這z我的不夠開採第0座金礦,那麼能挖出來的最多金子數就是0,因爲這惟一的金礦不夠人力去開採。讓咱們爲這些不需要別人的幫助就行準確地得出答案的人們鼓掌吧,這就是傳說中的底層勞動人民!        故事說到這裏先暫停一下,咱們現在又一次來分析一下這個故事,讓咱們對動態規劃有個理性認識。        子問題:        國王需要依據兩個大臣的答案以及第9座金礦的信息才幹推斷出最多能夠開採出多少金子。爲了解決本身面臨的問題,他需要給別人製造另外兩個問題,這兩個問題就是子問題。        思考動態規劃的第一點----最優子結構:        國王相信,僅僅要他的兩個大臣能夠回答出正確的答案(對於考慮能夠開採出的金子數,最多的也就是最優的同一時候也就是正確的),再加上他的聰明的推斷就必定能獲得終於的正確答案。咱們把這樣的子問題最優時母問題經過優化選擇後必定最優的狀況叫作「最優子結構」。        思考動態規劃的第二點----子問題重疊:        實際上國王也好,大臣也好,所有人面對的都是相同的問題,即給你必定數量的人,給你必定數量的金礦,讓你求出能夠開採出來的最多金子數。咱們把這樣的母問題與子問題本質上是同一個問題的狀況稱爲「子問題重疊」。然而問題中出現的不一樣點每每就是被子問題之間傳遞的參數,比方這裏的人數和金礦數。               思考動態規劃的第三點----邊界:        想一想假設不存在前面咱們提到的那些底層勞動者的話這個問題能解決嗎?永遠都不可能!咱們把這樣的子問題在必定時候就再也不需要提出子子問題的狀況叫作邊界,沒有邊界就會出現死循環。        思考動態規劃的第四點----子問題獨立:        要知道,當國王的兩個大臣在思考他們本身的問題時他們是不會關心對方是如何計算如何開採金礦的,因爲他們知道,國王僅僅會選擇兩我的中的一個做爲最後方案,還有一我的的方案並不會獲得實施,所以一我的的決定對還有一我的的決定是沒有影響的。咱們把這樣的一個母問題在對子問題選擇時,當前被選擇的子問題兩兩互不影響的狀況叫作「子問題獨立」。        這就是動態規劃,具備「最優子結構」、「子問題重疊」、「邊界」和「子問題獨立」,當你發現你正在思考的問題具有這四個性質的話,那麼恭喜你,你基本上已經找到了動態規劃的方法。        有了上面的這幾點,咱們就行寫出動態規劃的轉移方程式,現在咱們來寫出相應這個問題的方程式,假設用gold[mineNum]表示第mineNum個金礦能夠挖出的金子數,用peopleNeeded[mineNum]表示挖第mineNum個金礦需要的人數,用函數f(people,mineNum)表示當有people我的和編號爲0、一、二、三、……、mineNum的金礦時能夠獲得的最大金子數的話,f(people,mineNum)等於什麼呢?或者說f(people,mineNum)的轉移方程是如何的呢?        答案是: 當mineNum = 0且people >= peopleNeeded[mineNum]時 f(people,mineNum) = gold[mineNum]        當mineNum = 0且people < peopleNeeded[mineNum]時 f(people,mineNum) = 0        當mineNum != 0時 f(people,mineNum) = f(people-peopleNeeded[mineNum], mineNum-1) + gold[mineNum]與f(people, mineNum-1)中的較大者,前兩個式子相應動態規劃的「邊界」,後一個式子相應動態規劃的「最優子結構」請讀者弄明確後再繼續往下看。 ----第二節----動態規劃的長處--------               現在我若是讀者你已經搞清楚了爲何動態規劃是正確的方法,但是咱們爲何需要使用動態規劃呢?請先繼續讚揚這個故事:        國王得知他的兩個手下使用了和他一樣的方法去解決交代給他們的問題後,不但沒有以爲他的兩個大臣在偷懶,反而很是高興,因爲他知道,他的大臣一定會找不少其它的人一塊兒解決問題,而不少其它的人會找更不少其它的人,這樣他這個聰明的方法就會在不經意間流傳開來,而全國人民都會知道這個聰明的方法是他們偉大的國王想出來的,你說國王能不高興嗎?        但是國王也有一些擔心,因爲他實在不知道這個「project」要動用到多少人來完畢,假設幫助他解決問題的人太多的話那麼就太勞民傷財了。「會不會影響到今年的收成呢?」國王在內心想着這個問題,因而他請來了整個國家裏惟一的兩個數學天才,一個叫作小天,還有一個叫作小才。        國王問小天:「小天啊,我發覺這個問題有點嚴重,我知道事實上這可以簡單的當作一個組合問題,也就是從十個金礦中選取若干個金礦進行開採,看看哪一種組合獲得的金子最多,或許用組合方法會更好一些。你能告訴我一共同擁有多少種組合狀況嗎?」        「國王陛下,假設用組合方法的話一共要考慮2的10次方種狀況,也就是1024種狀況。」小天思考了一會回答到。        「嗯……,假設每一種狀況我交給一我的去計算能獲得的金子數的話,那我也要1024我的,事實上仍是挺多的。」國王好像再次感受到了本身的方法是正確的。        國王心理期待着小才能夠給它一個更好的答案,問到:「小才啊,那麼你能告訴我用個人那個方法總共需要多少人嗎?事實上,我也計算過,好像需要的人數是1+2+4+8+16+32+64+……,畢竟每一個人的確都需要找另外兩我的來幫助他們……」        不辜負國王的期待,小才微笑着說到:「親愛的國王陛下,事實上咱們並不需要那麼多人,因爲有很是多問題事實上是一樣的,而咱們僅僅需要爲每一個不一樣的問題使用一我的力即可。」        國王高興的問到:「此話怎樣講?」        「打個例如,假設有一我的需要知道1000我的和3個金礦可以開採出多少金子,同一時候還有一我的也需要知道1000我的和3個金礦可以開採出多少金子的話,那麼他們可以去詢問一樣的一我的,而不用各自找不一樣的人浪費人力了。」               國王思考着說到:「嗯,很是有道理,假設問題是同樣的話那麼就不需要去詢問兩個不一樣的人了,也就是說一個不一樣的問題僅需要一我的力,那麼一共同擁有多少個不一樣的問題呢?」           「因爲每個問題的人數可以從0取到10000,而金礦數可以從0取到10,因此最多大約有10000 * 10 等於100000個不一樣的問題。」 小才一邊算着一邊回答。        「什麼?十萬個問題?十萬我的力?」國王有點失望。        「請國王放心,其實咱們需要的人力遠遠小於這個數的,因爲不是每一個問題都會遇到,或許咱們僅需要1、兩百我的力就可以解決問題了,這主要和各個金礦所需要的人數有關。」 小才立馬回答到。        故事的最後,天然是國王再一次向他的臣民們證實了他是這個國家裏最聰明的人,現在咱們經過故事的第二部分來考慮動態規劃的另外兩個思考點。        思考動態規劃的第五點----作備忘錄:        正如上面所說的同樣,當咱們遇到一樣的問題時,咱們可以問同一我的。講的通俗一點就是,咱們可以把問題的解放在一個變量中,假設再次遇到這個問題就直接從變量中得到答案,所以每一個問題僅會計算一遍,假設不作備忘的話,動態規劃就沒有不論什麼優點可言了。                     思考動態規劃的第六點----時間分析:        正如上面所說,假設咱們用窮舉的方法,至少需要2^n個常數時間,因爲總共同擁有2^n種狀況需要考慮,假設在揹包問題中,包的容量爲1000,物品數爲100,那麼需要考慮2^100種狀況,這個數大約爲10的30次方。        而假設用動態規劃,最多大概僅僅有1000*100 = 100000個不一樣的問題,這和10的30次方比起來優點是很是明顯的。而實際狀況並不會出現那麼多不一樣的問題,比方在金礦模型中,假設所有的金礦所需人口都是1000我的,那麼問題總數大約僅僅有100個。        非正式地,咱們可以很是easy獲得動態規劃所需時間,假設共同擁有questionCount個一樣的子問題,而每一個問題需要面對chooseCount種選擇時,咱們所需時間就爲questionCount * chooseCount個常數。在金礦模型中,子問題最多有大概people * n 個(當中people是用於開採金礦的總人數,n是金礦的總數),所以questionCount = people * n,而就像國王需要考慮是採用左部下的結果仍是採用右部下的結果同樣,每一個問題面對兩個選擇,所以chooseCount = 2,因此程序執行時間爲 T = O(questionCount * chooseCount) =O(people * n),別忘了實際上需要的時間小於這個值,依據所遇到的詳細狀況有所不一樣。        這就是動態規劃的魔力,它下降了大量的計算,所以咱們需要動態規劃!                             ----第三節----動態規劃的思考角度----------               那麼什麼是動態規劃呢?我我的認爲,假設一個解決這個問題的方法知足上面六個思考點中的前四個,那麼這種方法就屬於動態規劃。而在思考動態規劃方法時,後兩點相同也是需要考慮的。        面對問題要尋找動態規劃的方法,首先要清楚一點,動態規劃不是算法,它是一種方法,它是在一件事情發生的過程當中尋找最優值的方法,所以,咱們需要對這件事情所發生的過程進行考慮。而一般咱們從過程的最後一步開始考慮,而不是先考慮過程的開始。        打個例如,上面的挖金礦問題,咱們可以以爲整個開採過程是從西至東進行開採的(也就是從第0座開始),那麼總有面對最後一座金礦的時候(第9座),對這座金礦不外乎兩個選擇,開採與不開採,在最後一步肯定時再去肯定倒數第二步,直到考慮第0座金礦(過程的開始)。        而過程的開始,也就是考慮的最後一步,就是邊界。        所以在遇到一個問題想用動態規劃的方法去解決時,最好仍是先思考一下這個過程是如何的,而後考慮過程的最後一步是如何選擇的,一般咱們需要本身去構造一個過程,比方後面的練習。 ----第四節----總結-------        那麼遇到問題怎樣用動態規劃去解決呢?依據上面的分析咱們可以依照如下的步驟去考慮:        一、構造問題所相應的過程。        二、思考過程的最後一個步驟,看看有哪些選擇狀況。        三、找到最後一步的子問題,確保符合「子問題重疊」,把子問題中不一樣樣的地方設置爲參數。        四、使得子問題符合「最優子結構」。        五、找到邊界,考慮邊界的各類處理方式。        六、確保知足「子問題獨立」,通常而言,假設咱們是在多個子問題中選擇一個做爲實施方案,而不會同一時候實施多個方案,那麼子問題就是獨立的。        七、考慮怎樣作備忘錄。        八、分析所需時間是否知足要求。        九、寫出轉移方程式。        ----第五節----練習-------        題目一:買書        有一書店引進了一套書,共同擁有3卷,每卷書訂價是60元,書店爲了搞促銷,推出一個活動,活動例如如下:               假設單獨購買當中一卷,那麼可以打9.5折。        假設同一時候購買兩卷不一樣的,那麼可以打9折。        假設同一時候購買三卷不一樣的,那麼可以打8.5折。               假設小明但願購買第1卷x本,第2卷y本,第3卷z本,那麼至少需要多少錢呢?(x、y、z爲三個已知整數)。        固然,這道題全然可以不用動態規劃來解,但是現在咱們是要學習動態規劃,所以請想一想怎樣用動態規劃來作?        答案:        一、過程爲一次一次的購買,每一次購買或許僅僅買一本(這有三種方案),或者買兩本(這也有三種方案),或者三本一塊兒買(這有一種方案),最後直到買全然部需要的書。        二、最後一步我一定會在7種購買方案中選擇一種,所以我要在7種購買方案中選擇一個最佳狀況。        三、子問題是,我選擇了某個方案後,怎樣使得購買剩餘的書能用最少的錢?並且這個選擇不會使得剩餘的書爲負數。母問題和子問題都是給定三卷書的購買量,求最少需要用的錢,因此有「子問題重疊」,問題中三個購買量設置爲參數,分別爲i、j、k。        四、的確符合。        五、邊界是一次購買就可以買全然部的書,處理方式請讀者本身考慮。        六、每次選擇最多有7種方案,並且不會同一時候實施當中多種,所以方案的選擇互不影響,因此有「子問題獨立」。        七、我可以用minMoney [j][k]來保存購買第1卷i本,第2卷j本,第3卷k本時所需的最少金錢。        八、共同擁有x * y * z 個問題,每個問題面對7種選擇,時間爲:O( x * y * z * 7) =   O( x * y * z )。        九、用函數MinMoney(i,j,k)來表示購買第1卷i本,第2卷j本,第3卷k本時所需的最少金錢,那麼有:                MinMoney(i,j,k)=min(s1,s2,s3,s4,s5,s6,s7),當中s1,s2,s3,s4,s5,s6,s7分別爲相應的7種方案使用的最少金錢:                s1 = 60 * 0.95 + MinMoney(i-1,j,k)                s2 = 60 * 0.95 + MinMoney(i,j-1,k)                s3 = 60 * 0.95 + MinMoney(i,j,k-1)                s4 = (60 + 60) * 0.9 + MinMoney(i-1,j-1,k)                s5 = (60 + 60) * 0.9 + MinMoney(i-1,j,k-1)                s6 = (60 + 60) * 0.9 + MinMoney(i-1,j,k-1)                s7 = (60 + 60 + 60) * 0.85 + MinMoney(i-1,j-1,k-1)               ----第六節----代碼參考------        如下提供金礦問題的程序源碼幫助讀者理解,並提供測試數據給你們練習。        輸入文件名稱爲「beibao.in」,因爲這個問題實際上就是揹包問題,因此測試數據文件名稱就保留原名吧。        輸入文件第一行有兩個數,第一個是國王可用用來開採金礦的總人數,第二個是總共發現的金礦數。        輸入文件的第2至n+1行每行有兩個數,第i行的兩個數分別表示第i-1個金礦需要的人數和可以獲得的金子數。        輸出文件僅一個整數,表示能夠獲得的最大金子數。        輸入例子:        100 5        77 92        22 22        29 87        50 46        99 90        輸出例子:        133
相關文章
相關標籤/搜索