很是特别的一个动态规划新手教程

很是特别的一个动态规划新手教程
今天在网上看到一个讲动态规划的文章,是以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
相关文章
相关标签/搜索