今天在网上看到一个讲动态规划的文章,是以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)中的较大者,前两个式子对应动态规划的“边界”,后一个式子对应动态规划的“最优子结构”请读者弄明白后再继续往下看。
----第二节----动态规划的优势--------
如今我假设读者你已经搞清楚了为何动态规划是正确的方法,可是咱们为何须要使用动态规划呢?请先继续欣赏这个故事:
国王得知他的两个手下使用了和他相同的方法去解决交代给他们的问题后,不但没有认为他的两个大臣在偷懒,反而很高兴,由于他知道,他的大臣必然会找更多的人一块儿解决这个问题,而更多的人会找更更多的人,这样他这个聪明的方法就会在不经意间流传开来,而全国人民都会知道这个聪明的方法是他们伟大的国王想出来的,你说国王能不高兴吗?
可是国王也有一些担心,由于他实在不知道这个“工程”要动用到多少人来完成,若是帮助他解决这个问题的人太多的话那么就太劳民伤财了。“会不会影响到今年的收成呢?”国王在内心想着这个问题,因而他请来了整个国家里惟一的两个数学天才,一个叫作小天,另外一个叫作小才。
国王问小天:“小天啊,我发觉这个问题有点严重,我知道其实这能够简单的当作一个组合问题,也就是从十个金矿中选取若干个金矿进行开采,看看哪一种组合获得的金子最多,也许用组合方法会更好一些。你能告诉我一共有多少种组合状况吗?”
“国王陛下,若是用组合方法的话一共要考虑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个。
非正式地,咱们能够很容易获得动态规划所需时间,若是共有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)
----第六节----代码参考------
html
动态规划虽然归结为是算法,但它给出的实际上是解决某一类问题的思路,并非有特定的代码或者数学公式。java
刚开始接触动态规划都会比较难理解,我本身理解也不是很深入,本着通俗易懂的原则,作个记录。算法
1.什么样的问题能够用动态规划数组
要用动态规划的问题仍是有很明显的特征的,最显著的就是子问题重叠,子问题重叠就是从最开始的问题引伸出来的ide
子问题都是和开始问题极其类似的,除了数据不一样其余问题形式都相同。拿采草药问题为例:函数
题目要求:学习
辰辰是个天资聪颖的孩子,他的梦想是成为世界上最伟大的医师。为此,他想拜附近最有威望的医师为师。医师为了判断他的资质,给他出了一个难题。医师把他带到一个处处都是草药的山洞里对他说:“孩子,这个山洞里有一些不一样的草药,采每一株都须要一些时间,每一株也有它自身的价值。我会给你一段时间,在这段时间里,你能够采到一些草药。若是你是一个聪明的孩子,你应该可让采到的草药的总价值最大。”
若是你是辰辰,你能完成这个任务吗?优化
首先开始问题简洁的说就是,在A时间内,要求采B个草药,使其采到全部草药的价值和最大。this
假设采了一株药草,花费的时间为D,先无论它的价值,那么此时子问题来了:spa
在A-D时间内,要求采B-1个草药,使其采到全部草药的价值和最大,这就是子问题重叠。
还须要注意的一点就是,子问题相互独立,所谓独立就是各个子问题得出的结果不会影响到其余问题的结果。好比
这里,采和不采这二者之间没有关联,假设有两我的都面临这个采草药的问题,他们都有一样的时间,采一样个数的
草药。甲采了第一个草药,乙没有采,两我的继续采后面的草药,此时乙不会由于甲采了草药而减小了本身采药的时
间,乙没有采,因此采第二个的时候,乙仍是原来开始时拥有的时间。这就是子问题独立。
2.怎么思考问题
接着上面的例子,B个草药每个面临的问题都是菜或者不采,因此给这些草药编号,假设B=5,那么就是有
0,1,2,3,4,这五个草药,这里用0或者1开始均可以。假如采第0个,那么此时时间减小了,还剩的采药时间为
原时间-采0花费的时间,采到的价值提升了=采0获得的价值,假如不采第0个,时间不变,价值不变。无论
对第0个选择哪一种操做,继续采第二个,可选操做同上。
那不可能一直选下去,有两个终止条件,第一是选到第4个之后,就没有草药再让你采了,第二是所剩余的时间已经
不够采了,其实仔细想一想,只有第一个终止条件成立。由于对于当前正在采的第n个草药,所剩余的时间不够,它还
是应该再去尝试第n+1个,那剩余时间和采第n+1所需时间比较,直到采到最后一个为止。
从这以上咱们就能够提取出改变的量有三个,采药的时间,获得的价值,采哪一个草药。
说了这么多,最重要的问题就是函数要怎么写,动态规划与递归是分不开的,从子问题重叠那里也能够看出来,是要
不断的递归函数,最终获得结果的。函数的参数就是从分析问题中提取出来的改变的量,但并不所有都是。
这里采草药获得的价值,是函数的返回值,不做为参数,时间和哪一个草药做为参数。有人可能会奇怪为何把价值当
作返回值,由于题目要的就是得出最大价值。
状态转移方程若是初学者去细抠这个状态的定义,是如何转移的,我的认为很差理解,建议拿几个动态规划的题目,
先了解思路,明白动态规划究竟是怎么样实现的,再去深思它的状态和状态转移。
还有一点须要注意:状态转移方程必定会递归调用上面的函数!
采n的式子:f(还有的时间-采n须要的时间,采第n+1个)+采n获得的价值
不采n的式子:f(还有的时间,采第n+1个)
回到问题,要求的是最大可采到的价值,这是个比较性的问题,须要选择一个最大值,在什么中选择呢?就是在采n
和不采n中得到的价值进行选择。在开始采的时候,每采一个都会有这样的问题,采第0个之后再继续采后面的草药获
得的价值较多,仍是不采第0个把时间留着后面的草药。固然你想是想不出来的,只能两条路都走,最后比较选一条
价值高的。因此咱们最终的状态转移方程就有了:
max.(f(还有的时间-采n须要的时间,采第n+1个)+采n获得的价值,f(还有的时间,采第n+1个) )
上面只是分析了一种状况,那就是采第n个草药时,剩余的时间充足,才能够有选择的采或者不采,那若是剩余的时
间已经不够采第n个草药了,就只能选择不采n的式子,也就是这里须要作一个判断。
写出状态转移方程之后,问题就基本解决了,可是当数据不少也很大的时候,须要使用备忘录方法提升效率。
3.如何使用备忘录
备忘录说白了就是记录已经算出来的数据,把这些数据保存下来,这样就不须要每次都进行大量的计算。
能够说对于复杂的数据来讲,没有备忘录的动态规划不具备任何优点。
上面没有讲到“最优子结构”的问题,最优子结构就是经过实现子问题的最优获得开始问题的最优解。
联系到上面,咱们比较两个式子从而获得最大值,可是这个最大值并无出来,而是把问题继续日后抛,在对
第0个草药选择采与不采之后,把问题抛给第1个草药。就这样一直日后面抛,直到第4个。那么在这个过程当中,
经过计算产生的数据,若是保存下来,在其余选择分支须要用到这个数据时,就能够直接获取。
保存格式因题而异,通常数据就是二维INT数组,数组的大小能够设置成容许范围的无限大,好比int[10000][10000]
但这样的设置,在数据很少的状况下,会很浪费空间。因此就想办法把范围大小和已经的条件关联起来。
回想在采药的问题中,它最初已经了草药的个数,每一个草药所须要的时间和价值,已经总的采药时间。
怎么从这些数据中选择,就是设置范围的关键。这须要考虑到作备忘录时的条件,也就是往数组里面放些什么,
在这道题目中,须要把采哪一个草药和时间放进去,注意这里的时间并非须要的时间,而是在采选择是否采这个草药
前剩余的时间。而后选择一个较大值,时间明显比哪一个草药要大,因此int[time*2][time*2]
这里*2是由于每一个药草对应两个不一样的状况。
1 package 洛谷; 2 3 import java.io.BufferedReader; 4 import java.io.IOException; 5 import java.io.InputStreamReader; 6 import java.util.ArrayList; 7 import java.util.Iterator; 8 9 public class P1048采药 10 { 11 ////////////////////////////////////////////////////////////////////////////// 12 // // 13 // 采摘n f(剩余时间-采摘时间,采n+1个)+n获得的价值 // 14 // 不采摘n f(剩余时间,采n+1个) // 15 // max( f(剩余时间-采摘时间,采n+1个)+n获得的价值 , f(剩余时间,采n+1个)); // 16 // // 17 ///////////////////////////////////////////////////////////////////////////// 18 // 最优子结构”、“子问题重叠”、“边界”和“子问题独立” [动态规划] 19 static int totaltime; 20 static int tatalnumber; 21 static ArrayList<Tbao> tbaos=new ArrayList<>();//全部草药 22 static int t[][];//记忆数组 23 public static void main(String[] args) throws IOException 24 { 25 BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in)); 26 String string[] = bufferedReader.readLine().split(" "); 27 totaltime = Integer.parseInt(string[0]); 28 tatalnumber = Integer.parseInt(string[1]); 29 for (int i = 0; i < tatalnumber; i++) 30 { 31 string = bufferedReader.readLine().split(" "); 32 tbaos.add(new Tbao(Integer.parseInt(string[0]), Integer.parseInt(string[1]))); 33 } 34 t=new int [totaltime*2][totaltime*2]; 35 System.out.println(find(0, totaltime)); 36 } 37 38 private static int find(int i,int time)//第几个 剩余时间 39 { 40 int result=0; 41 if (i==tatalnumber)//最后一个了 出口 42 { 43 return 0; 44 } 45 if(t[i][time]!=0) 46 { 47 return t[i][time]; 48 } 49 Tbao tbao=tbaos.get(i); 50 if(time>=tbao.time) 51 { 52 result= Math.max(find(i+1,time-tbao.time)+tbao.cost, find(i+1,time)); 53 } 54 else 55 { 56 result= find(i+1,time) ; 57 } 58 t[i][time]=result; 59 return result; 60 } 61 62 63 } 64 class Tbao 65 { 66 int time; 67 int cost; 68 public Tbao(int time, int cost) 69 { 70 super(); 71 this.time = time; 72 this.cost = cost; 73 } 74 @Override 75 public String toString() 76 { 77 return "Tbao [time=" + time + ", cost=" + cost + "]"; 78 } 79 80 }