当青训营遇上码上掘金 | 寻亲之旅+攒青豆

59 阅读3分钟

当青训营遇上码上掘金

今天是在掘金写文章的第二天。记录的是两道算法题

  1. 寻友之旅--二分法
  2. 攒青豆--单调栈

寻亲之旅

题目描述

小青要找小码去玩,他们的家在一条直线上,当前小青在地点 N ,小码在地点 K (0≤N , K≤100 000),并且小码在自己家原地不动等待小青。小青有两种交通方式可选:步行和公交。
步行:小青可以在一分钟内从任意节点 X 移动到节点 X-1 或 X+1
公交:小青可以在一分钟内从任意节点 X 移动到节点 2×X (公交不可以向后走)

输入

两个整数 N 和 K

输出

小青到小码家所需的最短时间(以分钟为单位)

解析

可以考虑的情况:

  1. 公交不能后退
  2. 小码原地不动

第一种情况

小青在小码前面

image.png 由于公交不能后退,只能步行这种情况,所以时间就是N-K分钟

第二种情况

小码在小青前面 image.png 当 N<K 时,小青的位置为N,小码的位置为K。假设N在k/2 和K 之间,那N要么到K位置,要么到K/2位置。进行丢比,到K/2的代价为N-K/2+1(k若为奇数则再加1),到K的代价为K-N。

攒青豆

题目描述

现有 n 个宽度为 1 的柱子,给出 n 个非负整数依次表示柱子的高度,排列后如下图所示,此时均匀从上空向下撒青豆,计算按此排列的柱子能接住多少青豆。(不考虑边角堆积)

image.png

输入

height = [5,0,2,1,4,0,1,0,3]

输出

17。上面是由数组 [5,0,2,1,4,0,1,0,3] 表示的柱子高度,在这种情况下,可以接 17 个单位的青豆。

解析

记录青豆可以分为两种方式,而这两种方式又有不同的方法

  1. 列记录(每列宽一米)
  2. 行记录(按行来记录)

方法:

  1. 双指针
  2. 动态规划
  3. 单调栈

双指针

此方法是按照列计算来的,每列宽1,我们可以寻找一列的最左边最高的高度,在寻找此列最右边最高的高度

int lheight = height[i];
int rheight = height[i];
for(int j=i+1;j<height.length;j++){
     rheight = Math.max(height[j],rheight);
           }
   for(int k=i;k>=0;k--){
     lheight = Math.max(height[k],lheight);
     }

左右最高的高度都找到后, h = Math.min(rheight,lheight)-height[i] 可以得出青豆数量。

需要注意的是:

  • 计算的h又可能为负数
  • 第一行和最后一行的青豆数量不用计算

动态规划

在上面的双指针中,我们计算了大量重复的左右最高高度,我们可以利用动态规划,记录在一个数组上,这样就可以避免重复技术。

最后的技术公式: min(左边柱子的最高高度,记录右边柱子的最高高度) - 当前柱子高度。 当前位置,左边的最高高度是前一个位置的左边最高高度和本高度的最大值。

即从左向右遍历:maxLeft[i] = max(height[i], maxLeft[i - 1]);

从右向左遍历:maxRight[i] = max(height[i], maxRight[i + 1]);

单调栈

用单调栈之前,先要知道这几点:

  1. 单调栈是按照行来计算
  2. 单调栈内的元素是有序的
  3. 栈内遇到相同元素扔弃旧元素,push新元素
  4. 栈内保存的值是下标