1. 程式人生 > >動態規劃——(很特別的一個動態規劃入門教程)#覺得原作者寫的很有意思

動態規劃——(很特別的一個動態規劃入門教程)#覺得原作者寫的很有意思

原文地址:http://www.cnblogs.com/sdjl/articles/1274312.html

今天在網上看到一個講動態規劃的文章,是以01揹包為例的,比之之前看到的相關文章都有很大的區別

/*考慮到我的部落格大概也只有我看了——這大概是一個收藏好東西的好地方*/

  對於動態規劃,每個剛接觸的人都需要一段時間來理解,特別是第一次接觸的時候總是想不通為什麼這種方法可行,這篇文章就是為了幫助大家理解動態規劃,並通過講解基本的01揹包問題來引導讀者如何去思考動態規劃。本文力求通俗易懂,無異性,不讓讀者感到迷惑,引導讀者去思考,所以如果你在閱讀中發現有不通順的地方,讓你產生錯誤理解的地方,讓你難得讀懂的地方,請跟貼指出,謝謝!

----第一節----初識動態規劃--------

       經典的01揹包問題是這樣的:

       有一個包和n個物品,包的容量為m,每個物品都有各自的體積和價值,問當從這n個物品中選擇多個物品放在包裡而物品體積總數不超過包的容量m時,能夠得到的最大價值是多少?[對於每個物品不可以取多次,最多隻能取一次,之所以叫做01揹包,0表示不取,1表示取]

       為了用一種生動又更形象的方式來講解此題,我把此題用另一種方式來描述,如下:

       有一個國家,所有的國民都非常老實憨厚,某天他們在自己的國家發現了十座金礦,並且這十座金礦在地圖上排成一條直線,國王知道這個訊息後非常高興,他希望能夠把這些金子都挖出來造福國民,首先他把這些金礦按照在地圖上的位置從西至東進行編號,依次為0、1、2、3、4、5、6、7、8、9,然後他命令他的手下去對每一座金礦進行勘測,以便知道挖取每一座金礦需要多少人力以及每座金礦能夠挖出多少金子,然後動員國民都來挖金子。

       題目補充1:挖每一座金礦需要的人數是固定的,多一個人少一個人都不行。國王知道每個金礦各需要多少人手,金礦i需要的人數為peopleNeeded[i]。

       題目補充2:每一座金礦所挖出來的金子數是固定的,當第i座金礦有peopleNeeded[i]人去挖的話,就一定能恰好挖出gold[i]個金子。否則一個金子都挖不出來。

       題目補充3:開採一座金礦的人完成開採工作後,他們不會再次去開採其它金礦,因此一個人最多隻能使用一次。

       題目補充4:國王在全國範圍內僅招募到了10000名願意為了國家去挖金子的人,因此這些人可能不夠把所有的金子都挖出來,但是國王希望挖到的金子越多越好。

       題目補充5:這個國家的每一個人都很老實(包括國王),不會私吞任何金子,也不會弄虛作假,不會說謊話。

       題目補充6:有很多人拿到這個題後的第一反應就是對每一個金礦求出平均每個人能挖出多少金子,然後從高到低進行選擇,這裡要強調這種方法是錯的,如果你也是這樣想的,請考慮揹包模型,當有一個揹包的容量為10,共有3個物品,體積分別是3、3、5,價值分別是6、6、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個人和除了第8、第9的其它所有金礦的話,你能告訴我你最多能挖出多少金子嗎?”

       然後國王的左部下繼續問另一個人:“如果我給你8500個人和除了第8、第9的其它所有金礦的話,你能告訴我你最多能挖出多少金子嗎?”

       國王的左部下在心裡想著:“如果他們倆都能回答我的問題的話,那國王交給我的問題不就解決了嗎?哈哈哈!”

       因為國王給了他的右部下10000個人,所以國王的右部下同樣也叫來了兩個人,對著其中一個人問:“如果我給你9000個人和除了第8、第9的其它所有金礦的話,你能告訴我你最多能挖出多少金子嗎?”

       然後國王的右部下繼續問他叫來的另一個人:“如果我給你10000個人和除了第8、第9的其它所有金礦的話,你能告訴我你最多能挖出多少金子嗎?”

       此時,國王的右部下同左部下一樣,他們都在為自己如此聰明而感到滿足。

       當然,這四個被叫來的人同樣自信地回答沒有問題,因為他們同樣地從這兩個大臣身上學到了相同的一點,而兩位自認為自己一樣很聰明的大臣得意地笑著回到了他們的府邸,等著別人回答他們提出來的問題,現在你知道了這兩個大臣是如何解決國王交待給他們的問題了嗎?

       那麼你認為被大臣叫去的那四個人又是怎麼完成大臣交給他們的問題的呢?答案當然是他們找到了另外八個人!

       沒用多少功夫,這個問題已經在全國傳開了,更多人的人找到了更更多的人來解決這個問題,而有些人卻不需要去另外找兩個人幫他,哪些人不需要別人的幫助就可以回答他們的問題呢?

       很明顯,當被問到給你z個人和僅有第0座金礦時最多能挖出多少金子時,就不需要別人的幫助,因為你知道,如果z大於等於挖取第0座金礦所需要的人數的話,那麼挖出來的最多金子數就是第0座金礦能夠挖出來的金子數,如果這z個人不夠開採第0座金礦,那麼能挖出來的最多金子數就是0,因為這唯一的金礦不夠人力去開採。讓我們為這些不需要別人的幫助就可以準確地得出答案的人們鼓掌吧,這就是傳說中的底層勞動人民!

       故事講到這裡先暫停一下,我們現在重新來分析一下這個故事,讓我們對動態規劃有個理性認識。

       子問題:

       國王需要根據兩個大臣的答案以及第9座金礦的資訊才能判斷出最多能夠開採出多少金子。為了解決自己面臨的問題,他需要給別人製造另外兩個問題,這兩個問題就是子問題。

       思考動態規劃的第一點----最優子結構:

       國王相信,只要他的兩個大臣能夠回答出正確的答案(對於考慮能夠開採出的金子數,最多的也就是最優的同時也就是正確的),再加上他的聰明的判斷就一定能得到最終的正確答案。我們把這種子問題最優時母問題通過優化選擇後一定最優的情況叫做“最優子結構”。

       思考動態規劃的第二點----子問題重疊:

       實際上國王也好,大臣也好,所有人面對的都是同樣的問題,即給你一定數量的人,給你一定數量的金礦,讓你求出能夠開採出來的最多金子數。我們把這種母問題與子問題本質上是同一個問題的情況稱為“子問題重疊”。然而問題中出現的不同點往往就是被子問題之間傳遞的引數,比如這裡的人數和金礦數。

       思考動態規劃的第三點----邊界:

       想想如果不存在前面我們提到的那些底層勞動者的話這個問題能解決嗎?永遠都不可能!我們把這種子問題在一定時候就不再需要提出子子問題的情況叫做邊界,沒有邊界就會出現死迴圈。

       思考動態規劃的第四點----子問題獨立:

       要知道,當國王的兩個大臣在思考他們自己的問題時他們是不會關心對方是如何計算怎樣開採金礦的,因為他們知道,國王只會選擇兩個人中的一個作為最後方案,另一個人的方案並不會得到實施,因此一個人的決定對另一個人的決定是沒有影響的。我們把這種一個母問題在對子問題選擇時,當前被選擇的子問題兩兩互不影響的情況叫做“子問題獨立”。

       這就是動態規劃,具有“最優子結構”、“子問題重疊”、“邊界”和“子問題獨立”,當你發現你正在思考的問題具備這四個性質的話,那麼恭喜你,你基本上已經找到了動態規劃的方法。

       有了上面的這幾點,我們就可以寫出動態規劃的轉移方程式,現在我們來寫出對應這個問題的方程式,如果用gold[mineNum]表示第mineNum個金礦能夠挖出的金子數,用peopleNeeded[mineNum]表示挖第mineNum個金礦需要的人數,用函式f(people,mineNum)表示當有people個人和編號為0、1、2、3、……、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)中的較大者,前兩個式子對應動態規劃的“邊界”,後一個式子對應動態規劃的“最優子結構”請讀者弄明白後再繼續往下看。

----第二節----動態規劃的優點--------

       現在我假設讀者你已經搞清楚了為什麼動態規劃是正確的方法,但是我們為什麼需要使用動態規劃呢?請先繼續欣賞這個故事:

       國王得知他的兩個手下使用了和他相同的方法去解決交代給他們的問題後,不但沒有認為他的兩個大臣在偷懶,反而很高興,因為他知道,他的大臣必然會找更多的人一起解決這個問題,而更多的人會找更更多的人,這樣他這個聰明的方法就會在不經意間流傳開來,而全國人民都會知道這個聰明的方法是他們偉大的國王想出來的,你說國王能不高興嗎?

       但是國王也有一些擔憂,因為他實在不知道這個“工程”要動用到多少人來完成,如果幫助他解決這個問題的人太多的話那麼就太勞民傷財了。“會不會影響到今年的收成呢?”國王在心裡想著這個問題,於是他請來了整個國家裡唯一的兩個數學天才,一個叫做小天,另一個叫做小才。

       國王問小天:“小天啊,我發覺這個問題有點嚴重,我知道其實這可以簡單的看成一個組合問題,也就是從十個金礦中選取若干個金礦進行開採,看看哪種組合得到的金子最多,也許用組合方法會更好一些。你能告訴我一共有多少種組合情況嗎?”

       “國王陛下,如果用組合方法的話一共要考慮2的10次方種情況,也就是1024種情況。”小天思考了一會回答到。

       “嗯……,如果每一種情況我交給一個人去計算能得到的金子數的話,那我也要1024個人,其實還是挺多的。”國王好像再次感覺到了自己的方法是正確的。

       國王心理期待著小才能夠給它一個更好的答案,問到:“小才啊,那麼你能告訴我用我的那個方法總共需要多少人嗎?其實,我也計算過,好像需要的人數是1+2+4+8+16+32+64+……,畢竟每一個人的確都需要找另外兩個人來幫助他們……”

       不辜負國王的期待,小才微笑著說到:“親愛的國王陛下,其實我們並不需要那麼多人,因為有很多問題其實是相同的,而我們只需要為每一個不同的問題使用一個人力便可。”

       國王高興的問到:“此話如何講?”

       “打個比方,如果有一個人需要知道1000個人和3個金礦可以開採出多少金子,同時另一個人也需要知道1000個人和3個金礦可以開採出多少金子的話,那麼他們可以去詢問相同的一個人,而不用各自找不同的人浪費人力了。”

       國王思考著說到:“嗯,很有道理,如果問題是一樣的話那麼就不需要去詢問兩個不同的人了,也就是說一個不同的問題僅需要一個人力,那麼一共有多少個不同的問題呢?”   

       “因為每個問題的人數可以從0取到10000,而金礦數可以從0取到10,所以最多大約有10000 * 10 等於100000個不同的問題。” 小才一邊算著一邊回答。

       “什麼?十萬個問題?十萬個人力?”國王有點失望。

       “請國王放心,事實上我們需要的人力遠遠小於這個數的,因為不是每一個問題都會遇到,也許我們僅需要一、兩百個人力就可以解決這個問題了,這主要和各個金礦所需要的人數有關。” 小才立刻回答到。

       故事的最後,自然是國王再一次向他的臣民們證明了他是這個國家裡最聰明的人,現在我們通過故事的第二部分來考慮動態規劃的另外兩個思考點。

       思考動態規劃的第五點----做備忘錄:

       正如上面所說的一樣,當我們遇到相同的問題時,我們可以問同一個人。講的通俗一點就是,我們可以把問題的解放在一個變數中,如果再次遇到這個問題就直接從變數中獲得答案,因此每一個問題僅會計算一遍,如果不做備忘的話,動態規劃就沒有任何優勢可言了。             

       思考動態規劃的第六點----時間分析:

       正如上面所說,如果我們用窮舉的方法,至少需要2^n個常數時間,因為總共有2^n種情況需要考慮,如果在揹包問題中,包的容量為1000,物品數為100,那麼需要考慮2^100種情況,這個數大約為10的30次方。

       而如果用動態規劃,最多大概只有1000*100 = 100000個不同的問題,這和10的30次方比起來優勢是很明顯的。而實際情況並不會出現那麼多不同的問題,比如在金礦模型中,如果所有的金礦所需人口都是1000個人,那麼問題總數大約只有100個。

       非正式地,我們可以很容易得到動態規劃所需時間,如果共有questionCount個相同的子問題,而每一個問題需要面對chooseCount種選擇時,我們所需時間就為questionCount * chooseCount個常數。在金礦模型中,子問題最多有大概people * n 個(其中people是用於開採金礦的總人數,n是金礦的總數),因此questionCount = people * n,而就像國王需要考慮是採用左部下的結果還是採用右部下的結果一樣,每個問題面對兩個選擇,因此chooseCount = 2,所以程式執行時間為 T = O(questionCount * chooseCount) =O(people * n),別忘了實際上需要的時間小於這個值,根據所遇到的具體情況有所不同。

       這就是動態規劃的魔力,它減少了大量的計算,因此我們需要動態規劃!

----第三節----動態規劃的思考角度----------

       那麼什麼是動態規劃呢?我個人覺得,如果一個解決問題的方法滿足上面六個思考點中的前四個,那麼這個方法就屬於動態規劃。而在思考動態規劃方法時,後兩點同樣也是需要考慮的。

       面對問題要尋找動態規劃的方法,首先要清楚一點,動態規劃不是演算法,它是一種方法,它是在一件事情發生的過程中尋找最優值的方法,因此,我們需要對這件事情所發生的過程進行考慮。而通常我們從過程的最後一步開始考慮,而不是先考慮過程的開始。

       打個比方,上面的挖金礦問題,我們可以認為整個開採過程是從西至東進行開採的(也就是從第0座開始),那麼總有面對最後一座金礦的時候(第9座),對這座金礦不外乎兩個選擇,開採與不開採,在最後一步確定時再去確定倒數第二步,直到考慮第0座金礦(過程的開始)。

       而過程的開始,也就是考慮的最後一步,就是邊界。

       因此在遇到一個問題想用動態規劃的方法去解決時,不妨先思考一下這個過程是怎樣的,然後考慮過程的最後一步是如何選擇的,通常我們需要自己去構造一個過程,比如後面的練習。

----第四節----總結-------

       那麼遇到問題如何用動態規劃去解決呢?根據上面的分析我們可以按照下面的步驟去考慮:

       1、構造問題所對應的過程。

       2、思考過程的最後一個步驟,看看有哪些選擇情況。

       3、找到最後一步的子問題,確保符合“子問題重疊”,把子問題中不相同的地方設定為引數。

       4、使得子問題符合“最優子結構”。

       5、找到邊界,考慮邊界的各種處理方式。

       6、確保滿足“子問題獨立”,一般而言,如果我們是在多個子問題中選擇一個作為實施方案,而不會同時實施多個方案,那麼子問題就是獨立的。

       7、考慮如何做備忘錄。

       8、分析所需時間是否滿足要求。

       9、寫出轉移方程式。

----第五節----練習-------

       題目一:買書

       有一書店引進了一套書,共有3卷,每卷書定價是60元,書店為了搞促銷,推出一個活動,活動如下:

       如果單獨購買其中一卷,那麼可以打9.5折。

       如果同時購買兩卷不同的,那麼可以打9折。

       如果同時購買三卷不同的,那麼可以打8.5折。

       如果小明希望購買第1卷x本,第2卷y本,第3卷z本,那麼至少需要多少錢呢?(x、y、z為三個已知整數)。

       當然,這道題完全可以不用動態規劃來解,但是現在我們是要學習動態規劃,因此請想想如何用動態規劃來做?

       答案:

       1、過程為一次一次的購買,每一次購買也許只買一本(這有三種方案),或者買兩本(這也有三種方案),或者三本一起買(這有一種方案),最後直到買完所有需要的書。

       2、最後一步我必然會在7種購買方案中選擇一種,因此我要在7種購買方案中選擇一個最佳情況。

       3、子問題是,我選擇了某個方案後,如何使得購買剩餘的書能用最少的錢?並且這個選擇不會使得剩餘的書為負數。母問題和子問題都是給定三卷書的購買量,求最少需要用的錢,所以有“子問題重疊”,問題中三個購買量設定為引數,分別為i、j、k。

       4、的確符合。

       5、邊界是一次購買就可以買完所有的書,處理方式請讀者自己考慮。

       6、每次選擇最多有7種方案,並且不會同時實施其中多種,因此方案的選擇互不影響,所以有“子問題獨立”。

       7、我可以用minMoney[i][j][k]來儲存購買第1卷i本,第2卷j本,第3卷k本時所需的最少金錢。

       8、共有x * y * z 個問題,每個問題面對7種選擇,時間為:O( x * y * z * 7) =  O( x * y * z )。

       9、用函式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

       參考程式碼如下:

/*
=========程式資訊========
對應題目:01揹包之金礦模型
使用語言:c++
使用編譯器:Visual Studio 2005.NET
使用演算法:動態規劃
演算法執行時間:O(people * n) [people是人數,n是金礦數]
作者:貴州大學05級 劉永輝 
暱稱:SDJL
編寫時間:2008年8月
聯絡QQ:44561907
E-Mail:[email protected]
獲得更多文章請訪問我的部落格:www.cnblogs.com/sdjl
如果發現BUG或有寫得不好的地方請發郵件告訴我:)
轉載請保留此部分資訊:)
*/

#include "stdafx.h"
#include <iostream>
#include <fstream>
using namespace std;

const int max_n = 100;//程式支援的最多金礦數
const int max_people = 10000;//程式支援的最多人數

int n;//金礦數
int peopleTotal;//可以用於挖金子的人數
int peopleNeed[max_n];//每座金礦需要的人數
int gold[max_n];//每座金礦能夠挖出來的金子數
int maxGold[max_people][max_n];//maxGold[i][j]儲存了i個人挖前j個金礦能夠得到的最大金子數,等於-1時表示未知

//初始化資料 
void init()
{
    ifstream inputFile("beibao.in");
    inputFile>>peopleTotal>>n;
    for(int i=0; i<n; i++)
        inputFile>>peopleNeed[i]>>gold[i];
    inputFile.close();
            
    for(int i=0; i<=peopleTotal; i++)
        for(int j=0; j<n; j++)
            maxGold[i][j] = -1;//等於-1時表示未知 [對應動態規劃中的“做備忘錄”]
        
}

//獲得在僅有people個人和前mineNum個金礦時能夠得到的最大金子數,注意“前多少個”也是從0開始編號的
int GetMaxGold(int people, int mineNum)
{
    //申明返回的最大金子數
    int retMaxGold;

    //如果這個問題曾經計算過  [對應動態規劃中的“做備忘錄”]
    if(maxGold[people][mineNum] != -1)
    {
        //獲得儲存起來的值
        retMaxGold = maxGold[people][mineNum];
    }
    else if(mineNum == 0)//如果僅有一個金礦時 [對應動態規劃中的“邊界”]
    {
        //當給出的人數足夠開採這座金礦
        if(people >= peopleNeed[mineNum])
        {    
            //得到的最大值就是這座金礦的金子數
            retMaxGold = gold[mineNum];
        }
        else//否則這唯一的一座金礦也不能開採
        {
            //得到的最大值為0個金子
            retMaxGold = 0;
        }
    }
    else if(people >= peopleNeed[mineNum])//如果給出的人夠開採這座金礦 [對應動態規劃中的“最優子結構”]
    {
        //考慮開採與不開採兩種情況,取最大值
        retMaxGold = max(GetMaxGold(people - peopleNeed[mineNum],mineNum -1) + gold[mineNum],
                                        GetMaxGold(people,mineNum - 1));
    }
    else//否則給出的人不夠開採這座金礦 [對應動態規劃中的“最優子結構”]
    {
        //僅考慮不開採的情況
        retMaxGold  = GetMaxGold(people,mineNum - 1);
    }
    
    //做備忘錄    
    maxGold[people][mineNum] = retMaxGold;
    return retMaxGold;
}

int _tmain(int argc, _TCHAR* argv[])
{
    //初始化資料
    init();
    //輸出給定peopleTotal個人和n個金礦能夠獲得的最大金子數,再次提醒編號從0開始,所以最後一個金礦編號為n-1
    cout<<GetMaxGold(peopleTotal,n-1);
    system("pause");
    return 0;
}

相關推薦

動態規劃——特別一個動態規劃入門教程#覺得原作者有意思

原文地址:http://www.cnblogs.com/sdjl/articles/1274312.html 今天在網上看到一個講動態規劃的文章,是以01揹包為例的,比之之前看到的相關文章都有很大的區別 /*考慮到我的部落格大概也只有我看了——這大概是一個收藏好東西的好地方

演算法課堂實驗報告——python動態規劃最長公共子序列LCS問題

python實現動態規劃 一、開發環境 開發工具:jupyter notebook 並使用vscode,cmd命令列工具協助程式設計測試演算法,並使用codeblocks輔助編寫C++程式 程式語言:python3.6 二、實驗內容 1.最長公共子序列問題。分別求x=

Robot Framework XPATH元素的定位如何獲取一個動態或具體的元素

添加 nbsp clas 初學者 提取 一位 驗證 方法 work 前提部分(可略過):對於初學者來說,元素定位的方式相對直接、粗糙一點。比如,用鼠標放在一個字符上點擊右鍵查看元素,或者先點擊F12再查看元素,大多情況下這種方式都是可行的。而我們最需要關註的也是容易阻塞我們

配置動態PATport address translation網絡地址轉換

generate nbsp stat ica asdm ado 驗證 ip add acl 思路與配置1.配置R1 2.配置雲 interface GigabitEthernet0 nam

如何自學人工智能路徑規劃附資源,百分百親身經驗

而且 文章 綜述 獨立性 -i 關於 ade sdn 其中 下面的每個資源都是我親身學過的,且是網上公開公認最優質的資源。 下面的每個學習步驟也是我一步步走過來的。希望大家以我為參考,少走彎路。 請大家不要浪費時間找非常多的資料,只看最精華的! 綜述,機器學習的自學簡

Java技術自學規劃學習路線圖、書籍、視訊總結

Java的火爆襲來,吸引了大批的初學者涉獵,想入行Java,卻苦於自己沒有基礎,擔心學不好。我告訴你,這些擔心都是浮雲,你只是懼怕走進一個新天地,人都是一樣的,面對自己不熟悉的領域總會有莫名的恐懼感。不是不可以,只是你缺乏的是勇敢邁出去的勇氣。 最近整理了一份關於Java自學的學習路線圖,希望幫

layui中radio的動態載入進入修改頁面時,設定radio

動態設定   radio  的值讓它處於選中狀態 效果圖 : 前端程式碼: <div class="layui-form-item"> <label class="layui-form-label">狀態</label>

CCF 201609-4 交通規劃 迪傑斯特拉+優先佇列

這道題我沒有好的思路,剛開始用的是spfa打的,結果不行。 之前沒見過迪傑斯特拉+佇列優化。 但這確實挺好用,。要是最小生成樹跟最短路結合起來的話用這個跑最好不過了。 所以對這道題非常適用。 #include<bits/stdc++.h> using n

KDD 2018 | Airbnb用機器學習實現房屋動態定價不看你就out啦!

在KDD 2018上,Airbnb的研究人員們發表了一篇名為Customized Regression Model for Airbnb Dynamic Pricing的論文。這篇文章描述了Airbnb使用的動態定價模型,以下是論智對文章的大致介紹。 價格優化的目的是幫助房東制定最優價格。傳統

洛谷2543AHOI2005]航線規劃 樹剖+線段樹+割邊思路

這個題的思路還是比較巧妙的。 首先,我們發現操作只有刪除和詢問兩種,而刪除並不好維護連通性和割邊之類的資訊。 所以我們不妨像WC2006水管局長那樣,將詢問離線,然後把操作轉化成加邊和詢問。 然後,我們會發現,若存在一條邊

前端探祕-靜態頁面與動態頁面靜態網站與動態網站

靜態頁面 網頁程式碼都在頁面中,不用執行asp , php , jsp , .net等程式生成客戶端程式碼不能自主管理髮布更新的頁面,如想更新內容,要通過FTP軟體把檔案DOWN下來用網頁製作軟體修

如何自學人工智慧路徑規劃附資源,百分百親身經驗

下面的每個資源都是我親身學過的,且是網上公開公認最優質的資源。 下面的每個學習步驟也是我一步步走過來的。希望大家以我為參考,少走彎路。 請大家不要浪費時間找非常多的資料,只看最精華的! 綜述,機器學習的自學簡單來說分為三個步驟 前期:知識儲備包括數學知識,機器學習經典

Retrofit 動態引數非固定引數、非必須引數Get、Post請求

關鍵詞:Retrofit 動態引數、非固定引數、非必須引數 有如下場景: 請求資料時: 1. 使用者未登入時,不帶引數userId; 2. 登入時帶上引數userId. 如下介面: @GET("index.php?r=defau

Java動態代理JDK介面代理和Cglib類代理

代理模式 代理模式是常用的java設計模式,它的特徵是代理類與委託類有同樣的介面,代理類主要負責為委託類預處理訊息、過濾訊息、把訊息轉發給委託類,以及事後處理訊息等。代理類與委託類之間通常會存在關聯關係,一個代理類的物件與一個委託類的物件關聯,代理類的物件本身並不真正實現服

BZOJ3589 動態樹鏈剖分+容斥原理

std class down ring print color 動態 inf while   顯然容斥後轉化為求樹鏈的交。這個題非常良心的保證了查詢的路徑都是到祖先的,求交就很休閑了。 #include<iostream> #include<cstdi

建立動態表格節點建立以及表格隔行變色

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html

導航面板[ExtJS4]ExtJS4左邊菜單面板收縮展開,右邊TabPanel內容動態更新解決寬度不能自適應問題

PS:今天上午,非常鬱悶,有很多簡單基礎的題問搞得我有些迷茫,哎,程式碼幾天不寫就忘。目前又不當COO,還是得用心記程式碼哦!     一.題問:     ExtJS4應用border佈局,邊左為accordion佈局的功能導航選單,發明當導航菜單面板縮收開展時,邊右選項卡

測試工具之Jmeter創建一個簡單測試用例

管理器 view time http ati 測試用例 停止 調度器 until 前面介紹了如何使用badboy錄制jmeter腳本,以及如何導入腳本並進行測試 這裏介紹下手動創建測試用例,主要步驟如下: 1、創建線程組 第一次打開Jmeter只有一個測試計劃,右鍵

idea工具的使用2以建立一個SpringBoot+MyBatis專案舉例

點選next,出現下圖:選擇你這個專案所需的模組依賴,我要建立的這個專案這裡需要Web中的Web模組的依賴;還需要SQL中的MySQL和MyBaties的依賴。點選next之後,出現下圖:一般來說,下圖中的專案名Project name和專案存放路徑Project location都是會根據

Java語言程式設計基礎篇第十版 程式設計練習題*3.11給出一個月的總天數

public class NumberOfDaysPerMonth { public static void main(String[] args) { // TODO Auto-generated method stub