寻友之旅-攒青豆

146 阅读4分钟

当青训营遇上码上掘金

寻友之旅

问题描述

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

请帮助小青通知小码,小青最快到达时间是多久?
输入: 两个整数 N 和 K
输出: 小青到小码家所需的最短时间(以分钟为单位)


解题思路

对于这题我写完后搜索了下其他人的解答,但似乎和我的想法不太一样,也不知道是不是我理解错题了,所以,以下仅供参考

小青和小码家在一条直线上如下(每个-符合代表一个节点):

-----小青--------. . . .-----小码----

对于移动有两个选择:

  • 坐公交
  • 走路

当小青还没到小码时,由于需要尽快到达,选择移动路程最多的方案

现在会出现一些情况:

  • 当选择移动后恰好到达小码所在地点

  • 当移动后会超过小码所在地点

    这是需要具体考虑的情况,由于此时移动距离太大,所以考虑换成步行一步一步走到小码处,但是步行也可以后退,所以当(坐公交移动+后退步行消耗的时间)< (直接步行时间)时,选择坐公交然后后退,反之选择直接步行


代码实现(Java版)

public class FindFriend {
    public int fastest(int N, int K) {
        int time = 0;
        while (N != K) {
            if (N == 0) {
                N = walkF(N);
                time++;
                continue;
            }
            // 当小青还没到小码时,由于需要尽快到达,选择移动路程最多的方案
            if (bus(N) >= walkF(N) && bus(N) <= K) {
                N = bus(N);
                time++;
            } else {
                //bus(N) > K 当移动后会超过小码所在地点
                //单纯走过去耗时
                int onlyWalkTime = K - N;
                //先坐车过去然后向后步行耗时, 公交车做一次用时1分钟
                int busAndWalk = 1 + (bus(N) - K);  
                time += Math.min(onlyWalkTime, busAndWalk);
                N = K;
            }
            
        }
        return time;
    }

    private int bus(int x) {
        return x * 2;
    }
    //go forward
    private int walkF(int x) {
        return x + 1;
    }
    //go backward
    // private int walkB(int x) {
    //     return x - 1;
    // }
}

样例输入

N K

0 1000

3 29

输出

35

8


其他语言代码实现

GO: code.juejin.cn/api/raw/718…

Python: code.juejin.cn/api/raw/718…

攒青豆

问题描述

现有 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,然后向右开始寻找第一个大于等于左边界的柱子,同时计算这个过程中遇到的柱子的总高度,因为找到右边界后这些低于左右边界的占位柱子就需要减去,但是存在一种情况就是右边没有一个柱子高度大于等于左边界,所以还需要一个变量,记录在寻找右边界过程中遇到的最高的柱子,同时还需要一个变量记录这个过程中占位柱子总高度,当大于等于左边界的右边界不存在时,将这个最高柱子作为右边界。

代码实现(Java)

public class GreenBean {

    public int maxWater(int[] pillars) {
        int total = 0;
        int left = 0;
        int right = 1;
        while (right < pillars.length) {
            //寻找右边界,右边界为除左边界外第一个大于等于右边界的柱子,
            //如果没有则这选择右边第一高度的柱子
            int[] rightAndTotal = rightBorder(left, pillars);
            right = rightAndTotal[0];
            int needToClear = rightAndTotal[1];
            total += Math.min(pillars[left], pillars[right]) * (right - left - 1) - needToClear;
            left = right++;
        }
        return total;
    }

    //寻找右边界,返回由右边界下标和桶内需要去除的占位所组成的二元数组
    // return {右边界下标, 占位数}
    
    private int[] rightBorder(int left, int[] pillars) {
        //最大柱子下标
        int maxIndex = left + 1;
        int right = left + 1;
        //桶内需要去除的柱子所占位置
        int needToClear = 0;
        //左边界与右边最大柱子之间占位柱子所耗空间
        int betweenMax = 0;
        while (right < pillars.length) {
            //记录最大高度柱子的下标
            if (pillars[maxIndex] <= pillars[right]) {
                betweenMax = needToClear;
                maxIndex = right;
            }
            if (pillars[left] <= pillars[right]) {
                return new int[]{right, needToClear};
            } else {
                // right++;
                needToClear += pillars[right++];
            }
            
        }
        return new int[]{maxIndex, betweenMax};
    }

    public static void main(String[] args) {
        int result = new GreenBean().maxWater(new int[] {6,4,5});
        System.out.println(result);
    }
}

样例输入

height

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

[8,1,6,1,4,1,2,1]

[1]

[11, 2, 4, 7, 18, 3, 8, 5, 9, 11]

输出:

17

9

0

39


其他语言实现

Go版: code.juejin.cn/api/raw/718…

Python版:code.juejin.cn/api/raw/718…