问题:有一个国家发现了5座金矿,每座金矿的黄金储量不同,需要参与挖掘的工人数也不同(情况如下图)

金矿编号 黄金储量 需要人数
1 400 5
2 500 5
3 200 3
4 300 4
5 350 3

首先,动态规划方法适合的题型4个基本特点是:
1、最优子结构,当前一个状态得到最佳解时,当前状态在前一个状态下一定有最佳解;
2、子问题重叠,每个状态下要解决的问题除参数不同外,其本质是一样的;
3、有边界,当解决了最后一个子问题时,整个问题得解;
4、子问题独立,解决一个子问题时不依赖于另一个同级的子问题,只与它的母问题有关;
当存在这四个特点时很大程度上可以确定用动态规划的方法解觉了。
而解决动态规划问题的关键在于写出状态转移方程式,一般来说,对应一个状态下,对某件事情是否执行,这是两个子问题,每个子问题都可以递归下一个状态,最终到达边界条件返回,再判断最开始的状态下两个子问题的最优解,即是整个问题的答案。

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

苦思良久,国王有一个好办法了,他想,我只要知道前4座金矿最多能开采多少金子就能计算出开采所有金矿最多能获得多少金子了。他对左丞相说我不开采第5座金矿,你想办法告诉我开采前4座金矿最多能获得多少金子,又对右丞相说我要开采第5座金矿,用掉100个劳力,你想办法告诉我开采前4座金矿最多能获得多少金子。

这下子,国王不急丞相急了,左丞相们想啊, 国王这么聪明,要我告诉他前4座金矿最多能开采多少金子,那我是不是也可以学学国王让大臣告诉我前三座金矿最多能挖多少金子呢,于是左丞相叫来两个大臣,对其中一个说,我要240人用于开采第4、5座金矿,其它人手给你调配的话,你告诉我前三座金矿最多能开采多少金子,又对另一个说我要用100人开采第5座金矿,第4座不开采,其它人手给你调配的话你告诉我前三座金矿最多能挖多少金子。

右丞相焦头烂额之际,打听到左丞相有此妙计,不禁豁然开朗,只要学着国王的做法,把前几座金矿的最大开采量交给属下去解决,我只要决定一座金矿是否开采得出较大值不就得到答案了吗?于是,右丞相也依法炮制,也叫两个大臣,让他们分别在开采与不开采第4座金矿的前提下调查前三座金矿的最大产量。

接下来,这个计算金矿最大开采量的办法被传开了,这个国家的人民,纷纷赞扬国王的聪明,他们把这种办法叫做国王的金矿。

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

解法:

我们来分析一下最优子结构和最终问题的关系。换句话说,4个金矿的最优选择和5个金矿的最优选择之间,是什么样的关系?

5个金矿的最优选择,就是(前4座金矿10工人的挖金数量)和(前4座金矿7工人的挖金数量+第5座金矿的挖金数量)的最大值。

下面,我们把金矿数量设为N,工人数设为W,金矿的黄金量设为数组G[],金矿的用工量设为数组P[]。

那么5座金矿和4座金矿的最优选择之间存在这样的关系:F(5,10) = MAX( F(4,10), F(4,10-P[4])+G[4] ) 注意:数组下标从0开始。

最后我们还需要确定一下,这个问题的边界是什么?

边界两种情况:(1)只有1座金矿,也就是N=1的时候。这时候没得选,只能挖这座唯一的金矿,得到的黄金数量就是G[0]。(2)如果给定的工人数量不够挖取第1座金矿,也就是 W < P[0] 的时候,那么得到的黄金数就是0了。

用公式来表达如下:

经过上面的分析,我们可以得出这个问题完整的状态转移方程:

F (N,W) = 0 (当N <= 1, W < P[0]);

F (N,W) = G[0] (当N == 1, W >= P[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]);

先贴出递归求解代码

//动态规划递归解法
// 题目:获得金矿最优收益
/*
w 表示工人数量
n 表示可选金矿数量
p 表示金矿开采需要的工人数量
g 表示金矿储量
*/
#include<iostream>
#include<cmath>
#include<cstring>
using namespace std;
int GetBest( int w,int n,int p[],int g[]){//工人数量为零或者没有东西可以采if(w==0||n==0){return 0;
}
//当工人不够挖掘第N个金矿时if(w<p[n-1]){return GetBest(w,n-1,p,g);}//在两种最有子结构中找到最有解,也就是最大值return max(GetBest(w,n-1,p,g),GetBest(w-p[n-1],n-1,p,g)+g[n-1]);}
int main(int argc, char const *argv[]) {int w=10;int p[]={5,5,3,4,3};int g[]={400,500,200,300,350};int length=sizeof(g)/sizeof(int);cout<<GetBest(w,length,p,g)<<endl;return 0;
}

非递归代码贴出,为什莫用非递归呢?为了提高算法运行效率。

int GetBest(int w,int length,int p[],int g[]){int resulttable[length+1][w+1]={0};// for(int i=0;i<length+1;i++){//   for(int j=0;j<w+1;j++){//     cout<<resulttable[i][j]<<" ";//   }//   cout<<endl;// }
memset(resulttable, 0, sizeof resulttable);for(int i=1;i<length+1;i++){for(int j=1;j<w+1;j++){if(j<p[i-1]){resulttable[i][j]=resulttable[i-1][j];}else{resulttable[i][j]=max(resulttable[i-1][j],(resulttable[i-1][j-p[i-1]]+g[i-1]));}}}cout<< resulttable[length][w]<<endl;return 0;
}

上面的代码的空间复杂度为o(nw),我们可以利用一维数组降低空间复杂度

这里有一个很重要的点就是内循环必须从后往前进行,因为从前往后进行覆盖的时候,后面的数据计算会根据前面覆盖后的数据进行计算,所以必须从后到前

void GetBest1(int w,int length,int p[],int g[]){int result[w+1];memset(result,0,sizeof result);for(int i=1;i<=length;i++){for(int j=w;j>=1;j--){if(j>=p[i-1]){result[j]=max(result[j],result[j-p[i-1]]+g[i-1]);}}}cout<<result[w]<<endl;
}

完整代码展示

#include<iostream>
#include<cmath>
#include<cstring>
using namespace std;
int GetBest(int w,int length,int p[],int g[]){int resulttable[length+1][w+1]={0};// for(int i=0;i<length+1;i++){//   for(int j=0;j<w+1;j++){//     cout<<resulttable[i][j]<<" ";//   }//   cout<<endl;// }
memset(resulttable, 0, sizeof resulttable);for(int i=1;i<length+1;i++){for(int j=1;j<w+1;j++){if(j<p[i-1]){resulttable[i][j]=resulttable[i-1][j];}else{resulttable[i][j]=max(resulttable[i-1][j],(resulttable[i-1][j-p[i-1]]+g[i-1]));}}}cout<< resulttable[length][w]<<endl;return 0;
}
void GetBest1(int w,int length,int p[],int g[]){int result[w+1];memset(result,0,sizeof result);for(int i=1;i<=length;i++){for(int j=w;j>=1;j--){if(j>=p[i-1]){result[j]=max(result[j],result[j-p[i-1]]+g[i-1]);}}}cout<<result[w]<<endl;
}
int main(int argc, char const *argv[]) {int w=10;int p[]={5,5,3,4,3};int g[]={400,500,200,300,350};int length=sizeof(g)/sizeof(int);GetBest(w,length,p,g);GetBest1(w,length,p,g);return 0;
}

【面试经典】求解金矿问题(动态规划初级)相关推荐

  1. 面试经典算法题集锦——《剑指 offer》小结

    本文转载自: http://www.cnblogs.com/fanling999/p/7810558.html 作者:fanling999 转载请注明该声明. 从今年 3 月份开始准备找实习,到现在校 ...

  2. java面试总结(一)-----如何准备Java初级和高级的技术面试

    java面试总结(一)--如何准备Java初级和高级的技术面试 本文内容来自:https://mp.weixin.qq.com/s?__biz=MzAxNDMwMTMwMw==&mid=224 ...

  3. stm32经典笔试题_嵌入式面试经典30问

    嵌入式面试经典30问 在经过4个多月的学习后,学员们最关心的问题莫过于如何拿到高薪offer问题了. 但是很多同学说很害怕面试,看见面试官会露怯,怕自己的知识体系不完整,怕面试官考的问题回答不上了,所 ...

  4. 计算机访学面试,国家公派访问学者面试经典问题总结

    原标题:国家公派访问学者面试经典问题总结 根据国家留学基金委CSC官方网站最新发布的2017国家公派留学出国访学计划,2017年国家公派高级研究学者.访问学者.博士后项目计划选派3500人.这也是众多 ...

  5. 数字IC设计工程师笔试面试经典100题-有答案

    转自知乎答主ictown_数字IC设计工程师笔试面试经典100题-有答案-陈恩 1:什么是同步逻辑和异步逻辑?(汉王) 同步逻辑是时钟之间有固定的因果关系.异步逻辑是各时钟之间没有固定的因果关系. 同 ...

  6. 计算机专业复试面试英语口语问题,考研复试英语口语面试经典问题集锦.doc

    考研复试英语口语面试经典问题集锦 各学校各专业的考研复试口语形式各不相同,要对症下药.口语考试一般分为两步走. 第一步:一篇500-600字的短文,在3-4分钟内阅读完,期间不可以做笔记,然后跑开短文 ...

  7. 【人事】62道开发人员面试经典题

    [人事]62道开发人员面试经典题 1.请介绍一下你自己 这是面试官100%会问的问题,一般人回答这个问题过于平常,只说姓名.年龄.爱好.所学专业等,如果你用一分钟来重复你的简历,那么,你的印象加分没有 ...

  8. Redis面试经典问题

    Redis面试经典问题 基础 1)Redis中的基本数据类型.应用场景以及它们的底层 2)高并发下缓存带来的问题,缓存穿透,缓存击穿.缓存雪崩 3)Redis为什么那么快 4)Redis事务 5)re ...

  9. 面试经典情景题:高并发解决方案

    面试经典情景题:高并发解决方案 情景模拟:在很多个用户同时访问网站的时候,例如:抢购或者双十一的时候.如何避免服务器宕机或者数据库挂掉的问题,请你提供几种解决方案. 1.静态资源与后台服务进行分离 静 ...

  10. 复旦计算机英语面试问题,北大、清华、人大、复旦英语面试经典35题

    北大.清华.人大.复旦英语面试经典35题 Questions: 1.Would you please make a brief introduction about yourself? 2.Why d ...

最新文章

  1. 济南python工资一般多少钱-济南python开发培训机构哪个好
  2. python-os模块
  3. 讲解web服务所涉及到的重要知识点
  4. spring boot访问templates目录下的html静态页面
  5. 简单总结PPPoE设置
  6. 博图组态显示未分配的设备_S71200CPU做IO共享设备的配置方法【图文】
  7. svn is already locked
  8. php 怎么显示emoji,php中emoji转码显示的实现代码
  9. 360手机助手pc版 v2.4.0.1265 官方版
  10. sar adc的常用指标(一)
  11. 听说今年金三银四变成金一银二了。
  12. STorM32 BGC 三轴云台学习(一) 通信协议分析
  13. 揭秘!腾讯程序员告诉你当今最热门的5门编程语言
  14. 中国互联网微博生死局及商业价值分析
  15. 输入十个国名 要求按字母顺序输出C语言,用C语言编程实现国家名称按序输出,要求键盘输入五个国家的名字,按字母顺序排列打印输出。...
  16. Inwook,Kong
  17. 谷歌研究员走火入魔事件曝光:认为AI已具备人格,被罚带薪休假,聊天记录让网友San值狂掉...
  18. 基于STM32的外围系统设计
  19. 集成公告|Moonbeam宣布与ShowMe集成
  20. 练习题源代码46——55

热门文章

  1. 阿里云服务器+腾讯域名进行域名解析
  2. 怎么在word里标上标和下标?
  3. ”Could not find tag for codec none in stream #0, codec not currently supported in container”的解决方法
  4. .sh(shell)文件打印文件夹下所有文件的文件名
  5. 天池训练营——基于人脸的常见表情识别(3)——模型搭建、训练与测试
  6. linux基础(四):shell简单命令;文件系统命令,系统操作命令,文本操作命令;文本处理命令
  7. Ubuntu安装Google Chrome,报NSS version的错误
  8. 外包公司干了不到 3 个月,我离职了
  9. Pycharm的快捷键
  10. Ubuntu解压缩命令详解