0-1背包问题和vivo运矿石问题

最近刷vivo运矿石笔试题,题目如下:

链接:https://www.nowcoder.com/questionTerminal/b58f922cc924478fa1e2dca2cc4f4eb7
来源:牛客网小v最近在玩一款挖矿的游戏,该游戏介绍如下:
1、每次可以挖到多个矿石,每个矿石的重量都不一样,挖矿结束后需要通过一款平衡矿车运送下山;
2、平衡矿车有左右2个车厢,中间只有1个车轮沿着导轨滑到山下,且矿车只有在2个车厢重量完全相等且矿石数量相差不超过1个的情况下才能成功运送矿石,否则在转弯时可能出现侧翻。
假设小v挖到了n(n<100)个矿石,每个矿石重量不超过100,为了确保一次性将n个矿石都运送出去,一旦矿车的车厢重量不一样就需要购买配重砝码。请问小v每次最少需要购买多少重量的砝码呢? (假设车厢足够放下这些矿石和砝码,砝码重量任选)

题目乍一看有点类似0-1背包问题,而0-1背包问题是一道典型的可以利用动态规划解决的问题。下面先来回顾一下利用动态规划解决0-1背包问题。

不想看的童鞋可以直接跳到最后,有通过牛客网所有测试用例的Java代码,我还把牛客网评论里贴的代码下的评论(那些代码有哪些测试用例没过的)进行了测试,也都过了。当然不排除我的代码也有某些条件没考虑到会有一些测试用例通不过,欢迎指正。


0-1背包问题

问题描述:有一个背包最多能放W千克的东西,现在有N个物品,每个物品都有对应的质量wt[i]和价值val[i](i = 1,2,...,N), 问该背包最多能装多少价值的东西,注意物品不能切割。

这是一道经典的动态规划问题。关于动态规划的问题,这一系列的文章讲得挺好的,可以参考一下:

动态规划解题套路框架​labuladong.gitbook.io

影响该问题的状态有两个:背包容量 和 可选的物品

选择是: 装 和 不装 进背包

因为有两个状态,所以我们需要一个二维的dp数组

dp[i][w]表示对于前i个物品,当前的背包承重为w,在这种情况下能装的最大价值为dp[i][w]

最终我们需要的答案就是dp[N][W]

初始状态:dp[0][w]=0 , dp[i][0]=0 , 很容易理解,当没有物品或者包没有承重时,最多能装的价值为0。

可以整理出如下的伪代码:

int dp[N+1][W+1];
dp[0][] = 0;
dp[][0] = 0;for i in [1..N]{for w in [1..W]{dp[i][w] = max(选择装i,选择不装i);}
}return dp[N][W];

接下来我们要考虑的就是max(选择装i,选择不装i)的具体取值了。

再强调一下dp[i][w]表示的是对于前i个物品,当前的背包承重为w,在这种情况下能装的最大价值

选择不装i:即,不装i价值最大,此时dp[i][w]=dp[i-1][w]

选择装i:即,此时最大的价值为物体i的价值加上 (前i-1个物体)在(背包容量为【当前容量w-物品i的重量】)的情况下的最大价值。换个角度说就是你装了i,就得考虑,剩余承重限制下的最大值。表示为dp[i][w]=dp[i-1][w-wt[i-1]] + val[i-1], 注意一维数组的下标i-1表示第i个物体的属性。

当然选择装物品i是有条件的,就是物品i的重量wt[i-1]不能大于当前承重w

完整代码如下:

public static int knapsack(int W,int N,int[] weight,int[] value){//初始化int[][] dp = new int[N+1][W+1];for (int i=0;i<=N;++i){dp[i][0] = 0;}for (int w=0;w<=W;++w){dp[0][w] = 0;}for (int i=1;i<=N;++i){for (int w=1;w<=W;++w){//只能选择不装if (w - weight[i-1] < 0){dp[i][w] = dp[i-1][w];} else {dp[i][w] = Math.max(dp[i-1][w-weight[i-1]]+value[i-1],dp[i-1][w]);}}}return dp[N][W];}


vivo运矿石问题

利用上面连接给出的解决动态规划问题的套路,首先明确状态,哪些值会对结果产生影响。与0-1背包问题类似,[可选择的矿石]和[容量]是和明显的两个状态,但是本题对矿石的数量也有限制,因此[拿取矿石数量]是第三个状态。

dp数组,是一个三维的布尔型数组。dp[i][j][k]表示从前i个矿石中拿j个,质量是否等于k。

因此可以整理出如下伪代码:

boolean dp[I+1][J+1][K+1]
初始化dp
for i in [0..I]{for j in [0..J]{for k in [0..K]{状态转移}}
}

接下来要考虑的就是初始化和状态转移了。

初始化很简单,因为我们对i的遍历是从0开始的,所以我们要找出i=0时所有为true的情况,显然只有dp[0][0]为true。

状态转移,我们只要考虑对下一层(dp[i+1][][])的影响,不要跳(比如当dp[i][j][k]为true时,dp[i+n][j][k]显然也为true,n为索引范围内的正整数),不然容易乱。可以发现,dp[i][j][k]为true时对下一层的影响有dp[i+1][j][k]为true,dp[i+1][j+1][k+weight[i]]表示把第i+1个也拿进去,weight[i]是因为索引是从0开始的。

上面的伪代码可以整理成如下:

boolean dp[I+1][J+1][K+1]
dp[0][0][0]=true;
for i in [0..I]{for j in [0..J]{for k in [0..K]{if(dp[i][j][k]){dp[i+1][j][k] = true;dp[i+1][j+1][k+weight[i]] = true;}}}
}

你以为到这儿就结束了?确实,动态规划的部分到这儿基本就完成了,但这道题目麻烦就在各种小的细节。

先说I, J, K的问题,

I表示输入数组的长度,没什么好说的。

J 只要数组长度的一半就可以了,因为左右两个矿车的矿石数量差值不能超过1,但是记得得向上取整。

K,取值输入数组的总和sum.

在状态转移的时候注意k+weight[i]不能超过索引范围。

最后得到了dp数组,我们求的就是dp[I][J][k]dp[I][J-1][k](I为奇数)为true的情况下的sum-k-k绝对值(砝码重量)的最小值。

完整的Java代码如下:

private static int solution(int[] input) {int I = input.length;int J = (int)Math.ceil(input.length/2.0);int sum = 0;for (int i=0;i<I;++i){sum += input[i];}int K = sum;boolean[][][] dp = new boolean[I+1][J+1][K+1];//初始化dp数组dp[0][0][0] = true;//选择for (int i=0;i<I;++i){for (int j=0;j<=J && j<=i;j++){for (int k=0;k<=K;++k){if (dp[i][j][k]){dp[i+1][j][k] = true;if (k+input[i] <= K && j<J){dp[i+1][j+1][k+input[i]] = true;}}}}}int ans = sum;int cur = sum;for (int k=K;k>=0;k--){if (dp[I][J][k]){cur = Math.abs(sum-k-k);ans = ans<cur ? ans : cur;}}if (I%2 != 0){for (int k=K;k>=0;k--){if (dp[I][J-1][k]){cur = Math.abs(sum-k-k);ans = ans<cur ? ans : cur;}}}return ans;}

max日期最大值为0_0-1背包问题和vivo运矿石问题相关推荐

  1. max日期最大值为0_excel函数技巧:MAX在数字查找中的应用妙招

    编按:看到标题,小伙伴们可能会纳闷了,MAX不是求最大值的函数吗?什么时候成了"查询界的黑马"?还让VLOOKUP都甘拜下风了?面对小伙伴们的夺命三连问,小编自然也有自己的理由.在 ...

  2. max日期最大值为0_Excel中一定要掌握的MAX函数用法

    秋风清,秋月明.落叶聚还散,寒鸦栖复惊.相思相见知何日,此时此夜难为情. - -[唐] 李白<秋风清> 在Excel中的MAX函数相信大家都不陌生,MAX函数不就是求一组数据中的最大值吗? ...

  3. max日期最大值为0_Excel中的MAX,这5种不一样的使用技巧,看你知道几个

    秋风清,秋月明.落叶聚还散,寒鸦栖复惊.相思相见知何日,此时此夜难为情 ----[唐] 李白<秋风清> 在Excel中的MAX函数相信大家都不陌生,MAX函数不就是求一组数据中的最大值吗? ...

  4. max日期最大值为0_C语言编写程序输出10个整数中最小值或最大值

    C语言编写程序输出10个整数中最小值或最大值 这是一个比较基础,同时也包含C语言中诸多特点的程序,仔细体会有很多的格式规则和技巧.其中包括:输入语句,输出语句,数组,分支结构,循环结构.希望广大C语言 ...

  5. max日期最大值为0_【SQL】SQL面试50题思路解答与分类整理(下)CASE与日期函数...

    题目快速查找索引 阅读指南 上篇 [第一部分]聚合函数(sum/avg/count/min/max) [第二部分]表连接(inner join/left join/right join/full jo ...

  6. max日期最大值为0_函数的最大值和最小值说课稿范文

    函数的最大值和最小值说课稿范文 作为一名辛苦耕耘的教育工作者,通常需要准备好一份说课稿,借助说课稿可以有效提升自己的教学能力.写说课稿需要注意哪些格式呢?下面是小编整理的函数的最大值和最小值说课稿范文 ...

  7. C语言试题三十五之找出一维整型数组元素中最大的值和它所在的下标,最大的值和它所在的下标通过形参传回。主函数中x是数组名,n 是x中的数据个数,max存放最大值,index存放最大值所在元素的下标。

    1. 题目 请编写一个函数function,它的功能是:找出一维整型数组元素中最大的值和它所在的下标,最大的值和它所在的下标通过形参传回.主函数中x是数组名,n 是x中的数据个数,max存放最大值,i ...

  8. oracle 查询日期最大值,sql语句查询每天数据中的最大值

    案例: 创建天气小时表和天气日期表 create table dbo.T_WeatherStreetDataHourly ( ID bigint identity(1, 1), DDATETIME d ...

  9. mysql max datetime_MYSQL在联接语句中选择MAX日期

    我正在尝试返回记录编号的历史位置 我所拥有的是: SELECT l.location, t.transaction_id, t.date_modified FROM transactions as t ...

最新文章

  1. 多线程:当你提交任务时,线程队列已经满了,这时会发生什么?
  2. 反射就是获取该类的.class文件里面的方法,属性
  3. 模版引擎总结之综合分析模版引擎
  4. 快速查询ABAP transport request lock status
  5. linux安装meld rpm,Linux meld安装
  6. 常量指针(指向常量的指针)和指针常量
  7. 图形界面组件实验的一点总结
  8. 30种SQL查询语句优化方法
  9. RDIFramework.NET ━ .NET快速信息化系统开发框架 V3.0 版新增消息管理
  10. 格子玻尔兹曼机(Lattice Boltzmann Method)系列5:LBM多相流实例之Shan-Chen模型
  11. java append()_append()方法的坑
  12. STM32 USB DFU功能
  13. 六西格玛dfss_六西格玛设计(DFSS)的方法和知识
  14. 测试工作中必备技能---思维导图你会吗? 10分钟让你精通思维导图!!!
  15. f.read()函数详解
  16. python连接数据库mysql失败_mysql数据库连接失败是什么原因
  17. Codeforces869E The Untended Antiquity
  18. 获取网页上没有下载链接的视频音频资源 火狐浏览器插件 Video DownloadHelper
  19. 达内python第一次月考题目_第一次月考试卷分析
  20. 转-超声波CX20106A的内部电路图

热门文章

  1. Linux平台高级调试和优化
  2. 根据excel背景色进行筛选
  3. H5 移动调试全攻略
  4. 手机App的服务器的开发流程
  5. 打印机局域网共享以及如何连接局域网内打印机
  6. js 判断是平年还是闰年
  7. 创建一个通讯录小项目代码
  8. Nodejs模块化开发
  9. 如何解决ORA-12638:身份证明检索失败
  10. 手机App安全性测试初探