每日力扣:403. 青蛙过河

package com.sample.suncht.algo;

import java.util.*;

/**
 * 403. 青蛙过河
 * <p>
 * 一只青蛙想要过河。 假定河流被等分为 x 个单元格,并且在每一个单元格内都有可能放有一石子(也有可能没有)。 青蛙可以跳上石头,但是不可以跳入水中。
 * <p>
 * 给定石子的位置列表(用单元格序号升序表示), 请判定青蛙能否成功过河(即能否在最后一步跳至最后一个石子上)。 开始时, 青蛙默认已站在第一个石子上,并可以假定它第一步只能跳跃一个单位(即只能从单元格1跳至单元格2)。
 * <p>
 * 如果青蛙上一步跳跃了 k 个单位,那么它接下来的跳跃距离只能选择为 k - 1、k 或 k + 1个单位。 另请注意,青蛙只能向前方(终点的方向)跳跃。
 * <p>
 * 请注意:
 * <p>
 * 石子的数量 ≥ 2 且 < 1100;
 * 每一个石子的位置序号都是一个非负整数,且其 < 231;
 * 第一个石子的位置永远是0。
 * 示例 1:
 * <p>
 * [0,1,3,5,6,8,12,17]
 * <p>
 * 总共有8个石子。
 * 第一个石子处于序号为0的单元格的位置, 第二个石子处于序号为1的单元格的位置,
 * 第三个石子在序号为3的单元格的位置, 以此定义整个数组...
 * 最后一个石子处于序号为17的单元格的位置。
 * <p>
 * 返回 true。即青蛙可以成功过河,按照如下方案跳跃:
 * 跳1个单位到第2块石子, 然后跳2个单位到第3块石子, 接着
 * 跳2个单位到第4块石子, 然后跳3个单位到第6块石子,
 * 跳4个单位到第7块石子, 最后,跳5个单位到第8个石子(即最后一块石子)。
 * 示例 2:
 * <p>
 * [0,1,2,3,4,8,9,11]
 * <p>
 * 返回 false。青蛙没有办法过河。
 * 这是因为第5和第6个石子之间的间距太大,没有可选的方案供青蛙跳跃过去。
 * 
 * 
 * 执行用时 : 63 ms, 在Frog Jump的Java提交中击败了58.82% 的用户
 */
public class CanCross403 {
    public boolean canCross(Integer[] stones) {
        if (stones == null || stones.length < 2) {
            return true;
        }

        if (stones.length == 2) {
            return stones[1] == 1;
        }

        int[] res = new int[1]; //判断是否达到了最后一个节点,如果达到了,说明青蛙可以过河

        Map<Integer, Boolean> map = new LinkedHashMap<Integer, Boolean>();
        allowJump(stones, 0, 0, map, res);

        return res[0] == 1;

    }

    private void allowJump(Integer[] stones, int pos, int jump, Map<Integer, Boolean> map, int[] res) {
        if (res[0] != 1 && pos >= stones.length - 1) {
            res[0] = 1;
            return;
        }

        //一开始使用"pos:jump:i"字符串作为缓存,但是其实只用“pos:jump”就可以了
        // 最后为了提高速度,改成整型位运算
        Integer mark = pos ^ (jump << 11);
        if (map.containsKey(mark)) {
            return;
        }

        for (int i = pos + 1; i < stones.length; i++) {
            int dist = stones[i] - stones[pos];

            //如果dist小于jump-1,需要继续执行
            if (dist < jump - 1) {
                continue;
            }

            if (dist <= jump + 1) {
                map.put(mark, true);
                if (res[0] != 1) { //如果青蛙已过河,则不需要再进行计算了
                    allowJump(stones, i, dist, map, res);
                }
            } else {
                //如果dist大于jump+1,那么后面都一定是大于jump+1,则直接退出,不需要计算
                map.put(mark, false);
                return;
            }
        }

    }


    public static void main(String[] args) {
        List<AlgoHelper.InputParams<Integer[], Boolean>> datas = new ArrayList<>();
        datas.add(new AlgoHelper.InputParams<>(new Integer[]{0, 1, 3, 5, 6, 8, 12, 17}, true));
        datas.add(new AlgoHelper.InputParams<>(new Integer[]{0, 1, 3, 5, 6, 8, 12}, true));
        datas.add(new AlgoHelper.InputParams<>(new Integer[]{0, 1, 2, 3, 4, 8, 9, 11}, false));
        datas.add(new AlgoHelper.InputParams<>(new Integer[]{0, 1, 4}, false));
        datas.add(new AlgoHelper.InputParams<>(new Integer[]{0, 3, 4, 10}, false));
        datas.add(new AlgoHelper.InputParams<>(new Integer[]{0, 2}, false));
        datas.add(new AlgoHelper.InputParams<>(new Integer[]{0, 1}, true));
        datas.add(new AlgoHelper.InputParams<>(new Integer[]{0, 1, 2}, true));
        datas.add(new AlgoHelper.InputParams<>(new Integer[]{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, true));
        datas.add(new AlgoHelper.InputParams<>(new Integer[]{0, 1, 2, 3, 4, 5, 6, 12}, false));
        datas.add(new AlgoHelper.InputParams<>(new Integer[]{0, 1, 3, 6, 10, 15, 16, 21}, true));
        AlgoHelper.assertResult(datas, new CanCross403()::canCross);
    }

}

结果:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值