题解 | #过河#

过河

https://www.nowcoder.com/practice/e69176faf03341829c325cbc0a5dcc1c

import java.util.*;

/**
 * NC308 过河
 * @author d3y1
 */
public class Solution {
    // t*(t-1)
    private int PERIOD;

    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     *
     * @param l int整型
     * @param s int整型
     * @param t int整型
     * @param nums int整型ArrayList
     * @return int整型
     */
    public int crossRiver (int l, int s, int t, ArrayList<Integer> nums) {
        return solution1(l, s, t, nums);
        // return solution2(l, s, t, nums);
        // return solution3(l, s, t, nums);
    }

    /**
     * 动态规划 + 数学法(离散化)
     *
     * dp[i]表示跳到位置i处最少需要踩到的石子数
     *
     *         { Math.min(dp[i], dp[i-j]+1)  , stoneSet.contains(i)  && s<=j<=t
     * dp[i] = {
     *         { Math.min(dp[i], dp[i-j])    , !stoneSet.contains(i) && s<=j<=t
     *
     * @param l
     * @param s
     * @param t
     * @param nums
     * @return
     */
    private int solution1(int l, int s, int t, ArrayList<Integer> nums){
        if(l <= t){
            return 0;
        }

        int result = 0;

        // 1 只能跳跃一种距离
        if(s == t){
            for(int stone: nums){
                if(stone%t==0 && stone<=l){
                    result++;
                }
            }

            return result;
        }

        // 2 可以跳跃多种距离
        // 离散化处理 <- l很大 m很小
        PERIOD = t * (t-1);
        Collections.sort(nums);
        int m = nums.size();
        int[] stones = new int[m];
        int distance;
        HashSet<Integer> stoneSet = new HashSet<>();
        for(int i=0; i<m; i++){
            if(i == 0){
                distance = nums.get(i);
                stones[i] = distance % PERIOD;
            }else{
                distance = nums.get(i)- nums.get(i-1);
                stones[i] = stones[i-1] + distance % PERIOD;
            }
            stoneSet.add(stones[i]);
        }
        distance = l - stones[m-1];
        l = stones[m-1] + distance % PERIOD;

        int[] dp = new int[l+t];
        Arrays.fill(dp, Integer.MAX_VALUE>>1);

        // init
        for(int i=s; i<=t; i++){
            if(stoneSet.contains(i)){
                dp[i] = 1;
            }else{
                dp[i] = 0;
            }
        }

        for(int i=t+1; i<l+t; i++){
            for(int j=s; j<=t; j++){
                if(dp[i-j] != Integer.MAX_VALUE>>1){
                    if(stoneSet.contains(i)){
                        dp[i] = Math.min(dp[i], dp[i-j]+1);
                    }else{
                        dp[i] = Math.min(dp[i], dp[i-j]);
                    }
                }
            }
        }

        result = Integer.MAX_VALUE;
        for(int i=l; i<l+t; i++){
            result = Math.min(result, dp[i]);
        }

        return result;
    }

    /**
     * 动态规划: OOM
     *
     * dp[i]表示跳到位置i处最少需要踩到的石子数
     * 
     *         { Math.min(dp[i], dp[i-j]+1)  , stoneSet.contains(i)  && s<=j<=t
     * dp[i] = {
     *         { Math.min(dp[i], dp[i-j])    , !stoneSet.contains(i) && s<=j<=t
     *
     * @param l
     * @param s
     * @param t
     * @param nums
     * @return
     */
    private int solution2(int l, int s, int t, ArrayList<Integer> nums){
        if(l <= t){
            return 0;
        }

        int result = 0;

        // 1 只能跳跃一种距离
        if(s == t){
            for(int stone: nums){
                if(stone%t==0 && stone<=l){
                    result++;
                }
            }

            return result;
        }

        // 2 可以跳跃多种距离
        HashSet<Integer> stoneSet = new HashSet<>();
        for(int num: nums){
            stoneSet.add(num);
        }

        // 此处所需内存过大
        int[] dp = new int[l+t];
        Arrays.fill(dp, Integer.MAX_VALUE>>1);

        // init
        for(int i=s; i<=t; i++){
            if(stoneSet.contains(i)){
                dp[i] = 1;
            }else{
                dp[i] = 0;
            }
        }

        for(int i=t+1; i<l+t; i++){
            for(int j=s; j<=t; j++){
                if(dp[i-j] != Integer.MAX_VALUE>>1){
                    if(stoneSet.contains(i)){
                        dp[i] = Math.min(dp[i], dp[i-j]+1);
                    }else{
                        dp[i] = Math.min(dp[i], dp[i-j]);
                    }
                }
            }
        }

        result = Integer.MAX_VALUE;
        for(int i=l; i<l+t; i++){
            result = Math.min(result, dp[i]);
        }

        return result;
    }

    /**
     * 动态规划: 超时
     *
     * dp[i]表示跳到位置i处最少需要踩到的石子数
     *
     *         { Math.min(dp[i], dp[i-j]+1)  , stoneSet.contains(i)  && s<=j<=t
     * dp[i] = {
     *         { Math.min(dp[i], dp[i-j])    , !stoneSet.contains(i) && s<=j<=t
     *
     * @param l
     * @param s
     * @param t
     * @param nums
     * @return
     */
    private int solution3(int l, int s, int t, ArrayList<Integer> nums){
        if(l <= t){
            return 0;
        }

        int result = 0;

        // 1 只能跳跃一种距离
        if(s == t){
            for(int stone: nums){
                if(stone%t==0 && stone<=l){
                    result++;
                }
            }

            return result;
        }

        // 2 可以跳跃多种距离
        HashSet<Integer> stoneSet = new HashSet<>();
        for(int num: nums){
            stoneSet.add(num);
        }

        int[] dp = new int[s+t];
        Arrays.fill(dp, Integer.MAX_VALUE>>1);

        // init
        for(int i=s; i<=t; i++){
            if(stoneSet.contains(i)){
                dp[i] = 1;
            }else{
                dp[i] = 0;
            }
        }
        for(int i=t+1; i<s+t; i++){
            for(int j=s; j<=t; j++){
                if(dp[i-j] != Integer.MAX_VALUE>>1){
                    if(stoneSet.contains(i)){
                        dp[i] = Math.min(dp[i], dp[i-j]+1);
                    }else{
                        dp[i] = Math.min(dp[i], dp[i-j]);
                    }
                }
            }
        }

        result = Integer.MAX_VALUE>>1;
        int len = t-s+1;
        // 使用队列似乎更佳
        for(int i=0; i<t; i++){
            dp[i] = dp[s+i];
        }
        // 此处循环耗时过多
        for(int i=s+t; i<l+t; i++){
            int curr = Integer.MAX_VALUE>>1;
            int start = (i-s)%t;
            int index;
            for(int j=0; j<len; j++){
                index = (start+j)%t;
                if(dp[index] != Integer.MAX_VALUE>>1){
                    curr = Math.min(curr, dp[index]);
                    if(stoneSet.contains(i)){
                        curr++;
                    }
                }
            }

            dp[start] = curr;

            if(i >= l){
                result = Math.min(result, curr);
            }
        }

        return result;
    }
}

全部评论

相关推荐

不愿透露姓名的神秘牛友
07-09 12:02
ssob上原来真有BOSS啊
硫蛋蛋:这种也是打工的,只不是是给写字楼房东打工
点赞 评论 收藏
分享
不愿透露姓名的神秘牛友
07-09 16:15
我应届生,去年10月份开始在这家公司实习,到今年10月份正好一年想(实习+试用期),在想要不要提前9月份就离职,这样好找工作些,但又差一个月满一年,又怕10月份国庆回来离职,容易错过了下半年的金九银十,到年底容易gap到年后
小破站_程序员YT:说这家公司不好吧,你干了快一年 说这家公司好吧,你刚毕业就想跑路说你不懂行情吧,你怕错过金九银十说 你懂行情吧,校招阶段在实习,毕业社招想换工作 哥们,我该怎么劝你留下来呢
应届生,你找到工作了吗
点赞 评论 收藏
分享
06-26 22:20
门头沟学院 Java
码农索隆:让你把简历发给她,她说一些套话,然后让你加一个人,说这个人给你改简历,然后开始卖课
我的求职精神状态
点赞 评论 收藏
分享
06-11 17:39
门头沟学院 Java
小呆呆的大鼻涕:卧槽,用户彻底怒了
点赞 评论 收藏
分享
不愿透露姓名的神秘牛友
07-07 18:05
哈哈哈哈哈感觉朋友找工作的已经疯掉了,直接上图
码农索隆:真老板娘:“我嘞个去,这不我当年的套路吗
点赞 评论 收藏
分享
评论
2
收藏
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务