题目解析

有一个国家发现了5座金矿,每座金矿的黄金储量不同,需要参与挖掘的工人数也不同。参与挖矿工人的总数是10人。每座金矿要么全挖,要么不挖,不能派出一半人挖取一半金矿。要求用程序求解出,要想得到尽可能多的黄金,应该选择挖取哪几座金矿?

第一座金矿含金500,需要5人;第二座金矿含金200,需要3人;第三座金矿含金300,需要4人;第四座金矿含金350,需要3人;第五座金矿含金400,需要5人。

排列组合

每个金矿要么挖要么不挖,共有2^5组合方法。通过枚举这些组合,选出最大产出的组合。组合方式可用一棵树示意。

自顶向下进行遍历分支(从根部到叶子的路径为一种组合情况),算出分支的人力和产出,得出最大的产出那种组合。

动态规划

问题建模

动态规划有三个核心元素:最优子结构、边界、状态转移方程。

  • 问题一:这个问题的最优子结构:

    • 如果我们剩下最后一个也就是第5个金矿要考虑了,这时候会出现几种情况呢?

      • 没有足够的人手,因此一定不能挖。因此答案是:挖前4个金矿,10个工人的最多金子数量,即(前4个金矿10工人的挖金数量)
      • 有足够的人手,但是可以选择挖或者不挖
        • 不挖,因此答案是:挖前4个金矿,10个工人的最多金子数量,即(前4个金矿10工人的挖金数量)
        • 选择挖,因此答案是:第5个金矿的挖金数量 + 挖前4个金矿,10-3工人的最多金子数量。
    • 因此,问题的最优子结构有两个:
      • 当所剩工人不够挖掘当前金矿时,只有一种最优子结构:

        • 挖前4金矿,10工人的最优选择
      • 当所剩工人足够挖掘当前金矿时,有两种最优子结构
        • 一个是4金矿10工人时的最优选择
        • 一个是4金矿10-3工人时的最优选择 (第5个金矿要用掉3个工人数)
  • 问题二:推导状态转移方程。要推导状态转移方程,就是要推导最优子结构和最终问题有什么关系呢?也就是说,4个金矿的最优选择和5个金矿的最优选择之间,是什么样的关系?

    • 关系:

      • 当人手不够挖第5个金矿时,答案肯定是: (前4个金矿10工人的挖金数量)
      • 当人手足够挖第5个金矿时,这时有两种子结构,需要考虑这两种子结构和第5做金矿的关系。不用想,选择收益最大的,也就是
      • 5个金矿的最优选择 ,就是(前4个金矿10工人的挖金数量) 和(前4个金矿7工人的挖金数量 + 第5座金矿的挖金数量)之间的最大值
    • 从而可以推导出状态转移方程。
      • 这里有三个变化的东西:第N个金矿,还有多少工人数,可以得到的最多金子数量。最多金子数量是目标,因此它应该是返回值,所以我们需要考虑两个变化维度,用数学语言来想,就是要求一个二维函数,假设为F(n, w),n为第i座金矿,w为当前所剩工人数量。
      • 我们把金矿数量设为N,工人数设为W,金矿的黄金量设为G[]、金矿的用工量设为P[]
      • 因此5金矿和4金矿的最优选择之间存在这样的关系:F(5, 10) = MAX(F(4, 10), F(4, 10 - P[4]) + G[4])
  • 问题三:这个问题的边界

    • 当金矿数为0或者工人数为0时,F(n, w) = 0
  • 问题的状态转移方程为

    • 设金矿数量是n,工人数量是w,金矿的含金量为数组g[],金矿所需开采人数设为数组p[],设F(n, w)为n个金矿,w个工人时的最佳收益(返回值为最佳收益),那么状态转移方程是:

      • 问题边界:金矿数为0或者工人数为0的情况:F(n, w) = 0(n = 0或者w = 0)
      • 当所剩工人不够挖当前金矿时,只有一种最优子结构F(n,w)=F(n-1,w),(n>=1,w<p[n-1])
      • 常规情况下有两种最优子结构(即挖当前金矿和不挖当前金矿):F(n,w)=max(F(n-1,w),F(n-1,w-p[n-1]+g[n-1])),(n>=1,w>=p[n-1])

简单来说,可以这么看

  • 当人数不足以挖金矿时,只能获得 0 黄金
  • 当人数只能够挖一座金矿时,选择最大黄金数的金矿
  • 当人数足够挖两个金矿时,选择是挖一座黄金量多的黄金,还是挖两座黄金量少的黄金

题目解析

/*** 获得金矿最优权益* @param w 工人数量* @param n 可选金矿数量* @param p 金矿开采所需的工人数量* @param g 金矿储量* @return  最优收益*/public static int getBestGoldMining(int w,int n,int[] p,int[] g){if(w==0||n==0){return 0;}if (w<p[n-1]){return getBestGoldMining(w,n-1,p,g);}return Math.max(getBestGoldMining(w,n-1,p,g),getBestGoldMining(w-p[n-1],n-1,p,g)+g[n-1]);}
/*** 获得金矿最优权益* @param w 工人数量* @param p 金矿开采所需的工人数量* @param g 金矿储量* @return  最优收益*/public static int getBestGoldMining(int w,int[] p,int[] g){//创建表格int[][] resultTable=new int[g.length+1][w+1];//填充表格for (int i = 1; i <=g.length; i++) {for (int j = 1; j <=w; j++) {if (j<p[i-1]){resultTable[i][j]=resultTable[i-1][j];}else {resultTable[i][j]=Math.max(resultTable[i-1][j],resultTable[i-1][j-p[i-1]]+g[i-1]);}}}//返回最后一个格子的值return resultTable[g.length][w];}

还可以进行空间上的优化,因为二维数组的每一行的结果都可以由上一行推导得出,因此我们不需要保存整张表格,只保存一行即可,在计算下一行时,进行覆盖替换即可。

/*** 获得金矿最优权益* @param w 工人数量* @param p 金矿开采所需的工人数量* @param g 金矿储量* @return  最优收益*/public static int getBestGoldMiningV3(int w,int[] p,int[] g){//创建当前结果int[] results=new int[w+1];//填充一维数组for (int i = 1; i <g.length; i++) {for (int j=w;j>=1;j--){if (j>=p[i-1]){results[j]=Math.max(results[j],results[j-p[i-1]]+g[i-1]);}}}//返回最后一个格子的值return results[w];}

动态规划是怎么填表

  • 表格的第一列表示给定前1-5个金矿的情况,也就是N的取值。
  • 表格的第一行表示给定的工人数,也就是W的取值
  • 表格的其余的空白格,表示给定N和W值对应的黄金获得数,也就是F(N, w)

下面我们来填表:

  • 第一个金矿有400金,需要5个工人。所以前4个格子都是0,因为人数不够。后面的格子都是400,因为只有这一个金矿可以挖
  • 第二个金矿有500金,需要5个工人。那么第二行的前四个格子怎么计算呢?因为W<5,也就是人数不够,所以F(N, W) = F(N - 1, W) = 0
  • 第二行的后6个格子怎么计算呢?因为W>=5,人数足够了,所以F(N, W) = MAX(F(N - 1, W),F(N - 1,W - 5)+ 500)。所以第五个格子的值是500
  • 需要注意的是第2行的第10个格子,也就是N=2,W=10的时候,F(N - 1, W) = 400,F(N - 1,W-5)=400,MAX(400,400+500)=900


这是一个典型的0-1背包问题,工人总数可以看为背包的容量,金矿的个数可以看为物品的个数,金矿的含金量可以看作物品的价值,金矿的使用工人数可以看作物品所占空间数,这样一来就变成了0-1背包问题.

我们知道,动态规划方法适合的题型的4个基本特点是:

  • 最优子结构:当前一个状态得到最佳解时,当前状态在前一个状态下一定有最佳解
  • 子问题重叠:每个状态下要解决的问题除了参数不同之外,其本质是一样的
  • 有边界:当解决了最后一个子问题时,整个问题得解
  • 子问题独立:解决一个子问题时不依赖于另一个同级的子问题,只与它的母问题有关

当存在这四个特点时很大程度上可以确定用动态规划的方法解决了。

而解决动态规划问题的关键在于写出状态方程。一般来说,对应一个状态下,对某件事情是否执行,这是两个子问题,每个子问题都可以递归到下一个状态,最终到达边界条件返回,再判断最开始状态下两个子问题的最优解,就是整个问题的答案。

这里使用国王的金矿的例子来解释动态规划的实现过程:

有一个国家的国王为了增强国力要开采已探明储量的5座金矿,开采每一座金矿所需的人员是固定的,而且为了能顺利将金矿开采又不耽误人民生活,国王决定只调配500人去挖金矿,要同时开采所有金矿,而且每个人民只开采一次,他要向国会说明开采金矿能带来多少金子,但是问题来了,由于没有足够的人手一次性把所有金矿都开采,怎么搞清能获得最多金子的数量是个难题。

国王是这样处理的:

  • 他对左丞相说我们不开采第5座金矿,你告诉我开采前4座金矿最多能获得多少金子,
  • 又对右丞相说我要开采第5座金矿,用掉100个劳力,你想办法告诉我开采前4座金矿最多能获得多少金子。
  • 然后在这两个答案中选择一个最佳的

左丞相也叫来两个大臣

  • 对其中一个说,我要240人用于开采第4、5座金矿,其它人手给你调配的话,你告诉我前三座金矿最多能开采多少金子
  • 又对另一个说我要用100人开采第5座金矿,第4座不开采,其它人手给你调配的话你告诉我前三座金矿最多能挖多少金子。

右丞相,也决定学国王的做法,把前几个金矿的最大开采量交给属下去解决,只决定一个金矿是否开采得出最大值。于是,他也找来两个大臣,让它们分别在开采和不开采第4个进程的前提下调查前三个金矿的最大收益。

从上面我们可以看出:

  • 知道第i-1个金矿的最大收益就一定是知道第i个金矿的最大收益,这就是最优子结构
  • 每个人要知道i个金矿的最大收益,就必须知道前i-1个金矿的最大收益,这就是子问题重叠
  • 最终当当考虑第1座金矿的最大产量时,只要看是否有足够人手开采第1座金矿,有的话,答案是已探明的储量,没有的话就是0,然后答案汇报到上级,上级再得出第2座金矿开采与不开采得出的较大产量,再往上汇报…,这就是边界
  • 每个人从上级得到的前提都是不同的,上级决定开不开采,再将这个前提之一告诉下属,而下属不需要考虑上级给另一个下属什么前提,这就是子问题独立

总结:

  • 子问题:国王需要根据两个大臣的答案以及最后一个金矿的信息才能判断出最多能开采出多少金子,为了解决自己的问题,它需要给别人制造另外两个问题,这两个问题就是子问题
  • 最优子结构:国王相信,只要它的两个大臣能够回答出正确的答案,再加上他聪明的判断就一定能得到最终的正确答案。我们把这种子问题最优时母问题通过优化选择后一定最优的情况叫做最优子结构
  • 子问题重叠:实际上国王也好,大臣也好,所有人面对的都是同样的问题,即给你一定数量的人,给你一定数量的金矿,让你求出能够开采出来的最多金子数。我们把这种母问题和子问题本质上是同一个问题的情况叫做子问题重叠。然而问题中出现的不同点就是被子问题之间传递的参数,比如这里的人数和金矿树
  • 边界:子问题在一定时候就不需要提出子子问题的情况叫做边界,如果没有边界就会出现死循环
  • 子问题独立:要知道,当国王的两个大臣在思考它们自己的子问题时他们是不会关心对方是如何计算怎样开采金矿的,因为他们知道,国王只会选择两个人中的一个作为最后方案,另一个人的方案并不会被采用,因此一个人的决定对另一个人的决定是没有影响的。我们把这种一个母问题在对子问题选择时,当前被选择的子问题两两互不影响的情况叫做“子问题独立”
  • 备忘录:当我们遇到相同的问题时,我们可以问同一个人。讲的通俗一点就是,我们可以把问题的解放在一个变量中,如果再次遇到这个问题就直接从变量中获得答案,因此每一个问题仅会计算一遍,如果不做备忘的话,动态规划就没有任何优势可言了。

总结:

  • 我们必须要明确的是,动态规划它不是算法,它只是一种方法,它是在一件事情发生的过程中寻找最优解的方法。因此,我们需要对这件事情所发生的过程进行考虑。而通常我们从过程的最后一步开始考虑,而不是先考虑过程的开始
  • 而过程的开始,也就是考虑的最后一步,就是边界。

动态规划:国王与金矿相关推荐

  1. java经典问题国王_动态规划-国王的金矿问题java

    紧接着上一篇动态规划问题,现在我们开始探讨一个新的问题,问:有一个发现了5个金矿,每一个金矿的储量不同,需要参与挖掘的工人数也不通,参与挖矿工人的总数量是10人,每一座金矿要么全挖,要么不挖,不能派一 ...

  2. 动态规划入门之国王的金矿

    最近学习算法,对动态规划不太了解,使用的时候照搬转移方程式,知其然不知其所以然,今天看到一篇动态规划的教程,解释得非常通俗,原文在这里[动态规划入门教程] (http://blog.csdn.net/ ...

  3. 一文弄懂动态规划(DP Dynamic Programming)下楼梯,国王和金矿,背包问题,Dijkstra算法

    动态规划 参考链接 漫画算法,什么是动态规划? DP 动态规划是一种分阶段求解决策问题的数学思想 题目一 问:下楼梯问题,有一座高度是10级台阶的楼梯,从下往上走,每跨一步只能向上1级或者2级台阶,请 ...

  4. 0-1背包问题 题目:国王和金矿问题 描述:有一个国家发现了max_n座金矿,参与挖矿工人的总数是max_people人。每座金矿的黄金储量不同为一维数组gold[],需要参与挖掘的工人数也不同为一维

    题目四:0-1背包问题 题目:国王和金矿问题 描述:有一个国家发现了max_n座金矿,参与挖矿工人的总数是max_people人.每座金矿的黄金储量不同为一维数组gold[],需要参与挖掘的工人数也不 ...

  5. h0156.国王的金矿

    国王的金矿 作者 黄正鹏 单位 贵州工程应用技术学院 国王在他的国家发现了N座金矿,为了描述方便,我们给他们从1到N编号. 对于第i个金矿,需要投入Ci个的费用,能挖出来Wi个单位的金子. 现在国王想 ...

  6. 动态规划经典例题-国王的金矿问题

    金矿问题 问题概述: 有一位国王拥有5座金矿,每座金矿的黄金储量不同, 需要参与挖掘的工人人数也不同.例如有的金矿储量是500kg黄金,需 要5个工人来挖掘:有的金矿储量是200kg黄金,需要3个工人 ...

  7. 动态规划学习-【国王和金矿】

    微信公众号 题目: 有一个国家发现了5座金矿,每座金矿的黄金储量不同,需要参与挖掘的工人数也不同.参与挖矿工人的总数是10人.每座金矿要么全挖,要么不挖,不能派出一半人挖取一半金矿.要求用程序求解出, ...

  8. 动态规划简单例子——国王与金矿(c++)

    动态规划的要点:确定全局最优解和最优子结构之间的关系,以及问题的边界.以数字的形式表达就是状态转移方程式.下面以一个例子来对他们进行描述. 问题描述: 有一个国家发现了5座金矿,每座金矿的黄金储量不同 ...

  9. 【动态规划模型】金矿模型理解动态规划!(精彩的故事)

    对于动态规划,每个刚接触的人都需要一段时间来理解,特别是第一次接触的时候总是想不通为什么这种方法可行,这篇文章就是为了帮助大家理解动态规划,并通过讲解基本的01背包问题来引导读者如何去思考动态规划.本 ...

最新文章

  1. mybatis中config.xml文件的解析
  2. 题目1188:约瑟夫环
  3. BLOOMBERG how to start?
  4. 用SQL语句添加删除修改字段及一些表与字段的基本操作 .
  5. db2查最新值的前一天值_现在的C1驾照值多少钱?最新价格曝光,老司机一看赚翻了...
  6. eoLinker-API_Shop_验证码识别与生成类API调用的代码示例合集:六位图片验证码生成、四位图片验证码生成、简单验证码识别等...
  7. c++十进制转二进制_二进制与十进制如何互相转换?
  8. aardio教程_官方AARDIO课程已经开课了!学习的速来报道!
  9. CommonJS模块的循环加载
  10. RUP大讲堂(第五讲)-基于用例的需求工程技术
  11. ajax(form)图片上传(spring)
  12. web调用IC卡读卡器开发第七章--NFC标签NDEF数据
  13. 提高软件开发工作效率的几种方法
  14. 庞加莱买面包的故事(二)
  15. Lacuncher3---修改文件夹图标和修改桌面布置
  16. 两种 Type-C 耳机:模拟耳机 数字耳机
  17. 苹果手机如何投屏到电视机?新手一看就懂教程
  18. 计算机课怎么上,怎样上微机课初探
  19. dia 导出大分辨率高清png图
  20. 【牛客网SQL篇】非技术快速入门

热门文章

  1. 更改电脑桌面小图标——更好的摸鱼小技巧
  2. 怎么把avi格式转换成mp4格式?转换avi格式的方法
  3. 【在ubuntu实现本地与虚拟机文件的传输】
  4. 有效减小虚拟机占的内存
  5. VUE实战-知乎日报(2)
  6. php对接xenserver,XenServer 虚拟化应用总结
  7. XenServer 存储、vApp、高可用
  8. 双11的大型电商活动服务器崩溃是怎么回事?
  9. R:断点回归分析设计
  10. 2015年元旦百度新年大K站的情报汇总