LeetCode Top 100 Liked Questions 198. House Robber (Java版; Easy)

welcome to my blog

LeetCode Top 100 Liked Questions 198. House Robber (Java版; Easy)

题目描述
You are a professional robber planning to rob houses along a street. Each house has a certain amount of 
money stashed, the only constraint stopping you from robbing each of them is that adjacent houses have
security system connected and it will automatically contact the police if two adjacent houses were broken
into on the same night.

Given a list of non-negative integers representing the amount of money of each house, determine the 
maximum amount of money you can rob tonight without alerting the police.

Example 1:

Input: [1,2,3,1]
Output: 4
Explanation: Rob house 1 (money = 1) and then rob house 3 (money = 3).
            Total amount you can rob = 1 + 3 = 4.
Example 2:

Input: [2,7,9,3,1]
Output: 12
Explanation: Rob house 1 (money = 2), rob house 3 (money = 9) and rob house 5 (money = 1).
            Total amount you can rob = 2 + 9 + 1 = 12.
class Solution {
    public int rob(int[] nums) {
        int n = nums.length;
        if(n==0){
            return 0;
        }
        //dp[i][0]   dp[i][1]
        //dp[i][0] = max(dp[i-1][0], dp[i-1][1])
        //dp[i][1] = dp[i-1][0]+nums[i]
        int[][] dp = new int[n][2];
        dp[0][1] = nums[0];
        for(int i=1; i<n; i++){
            dp[i][0] = Math.max(dp[i-1][0], dp[i-1][1]);
            dp[i][1] = dp[i-1][0]+nums[i];
        }
        return Math.max(dp[n-1][0], dp[n-1][1]);
    }
}
第二次做; 三种动态规划的含义: 1)dp[i]表示抢nums[i]时的最大收益, 时间复杂度O(N^2) 2)dp[i]表示合理抢劫前i个房间时的最大收益 3)dp[i][0]表示[0,i]范围上,不抢nums[i]时的最大收益, dp[i][1]表示[0,i]范围上,抢nums[i]时的最大收益, 时间复杂度O(N); 核心: 1)两种类型的状态: 抢劫到第几户; 选还是不选
/*
跟股票题很像, 相当于当天买入后不能第二天卖出
*/
class Solution {
    //时间复杂度O(N)
    public int rob(int[] nums) {
        if(nums.length==0){
            return 0;
        }
        /*
        dp[i][0]表示[0,i]范围上,不抢nums[i]时的最大收益
        dp[i][1]表示[0,i]范围上,抢nums[i]时的最大收益

        dp[i][0] = max(dp[i-1][1], dp[i-1][0])
        dp[i][1] = dp[i-1][0] + nums[i]
        */
        int n = nums.length, max=0;
        int[][] dp = new int[n][2];
        dp[0][1] = nums[0];
        for(int i=1; i<n; i++){
            dp[i][0] = Math.max(dp[i-1][0], dp[i-1][1]);
            dp[i][1] = dp[i-1][0] + nums[i];
        }
        return Math.max(dp[n-1][0], dp[n-1][1]);
    }

    //时间复杂度O(N)
    public int rob2(int[] nums) {
        if(nums.length==0){
            return 0;
        }
        /*
        dp[i]表示合理抢劫前i个房间时的最大收益; 使用前i个这种含义的dp时有两个地方容易出错: 1)循环终止条件 2)第i个元素的索引是i-1
        dp[i] = max(dp[i-1], dp[i-2] + nums[i-1])
        */
        int n = nums.length, max=0;
        int[] dp = new int[n+1];
        dp[1] = nums[0];
        for(int i=2; i<=n; i++){
            dp[i] = Math.max(dp[i-1], dp[i-2]+nums[i-1]);
        }
        return dp[n];
        
    }

    //时间复杂度O(N^2)
    public int rob2(int[] nums) {
        /*
        dp[i]表示抢nums[i]时的最大收益
        dp[i] = max{dp[j] + nums[i] | j<=i-2}
        */
        int n = nums.length, max=0;
        int[] dp = new int[n];
        for(int i=0; i<n; i++){
            dp[i] = nums[i];
            for(int j=0; j<=i-2; j++){
                dp[i] = Math.max(dp[i], dp[j] + nums[i]);
            }
            max = Math.max(max, dp[i]);
        }
        return max;
    }
}
第一次做; 体验暴力递归, 超时无法通过; 主要是体会暴力递归和动态规划之间的区别, 暴力递归的思路比较直接, 相当于正向思维; 动态规划的转移方程不是很直接, 相当于逆向思维; 通过暴力递归的思路, 获取动态规划的思路, 比如根据核心式子return Math.max(nums[index] + core(nums, index+2), core(nums, index+1));领悟出当前状态只取决于前两个状态, 进而去写转移方程f(i)=Math.max(f(i-2)+nums[i], f(i-1))
/*
体验一下暴力递归(回溯?); 超时不能通过
*/
class Solution {
    public int rob(int[] nums) {
        if(nums==null||nums.length==0)
            return 0;
        return core(nums, 0);
    }
    /*
    递归函数逻辑: 返回[0,index]各种可能的最大值
    疑惑点: 最大值怎么表示, 直接用函数的返回值表示就行, 因为函数的返回值不需要做其他事情, 比如LeetCode 114题就需要做其他事情
    */
    public int core(int[] nums, int index){
        //base case
        if(index>=nums.length)
            return 0;
        return Math.max(nums[index] + core(nums, index+2), core(nums, index+1));
    }
}
第一次做; 动态规划; 选当前状态, 不选当前状态; f(i)表示[0,…,1]上各种可能的最大值; 抢nums[i]时, f(i)=f(i-2)+nums[i]. 不抢nums[i]时, f(i)=f(i-1); 疑惑写在注释中了, 解决疑惑: 抢nums[i]时也可以从f(i-1)转移到f(i),只要没有抢nums[i-1]就行, 只不过不抢nums[i-1]的话, f(i-1)==f(i-2), 还是相当于从f(i-2)转移到f(i)
  • 时间复杂度O(N)
  • 空间复杂度O(1)
/*
动态规划: 已有0,...,i-1
f(i)表示0,...,i上的最大和
如何到达f(i)? 只有两种选择:
    不抢i的话, f(i)=f(i-1) 
    抢i的话, f(i) = f(i-2) + nums[i]
开始时, 最大的疑惑是, 如果实现f(i-1)时没有抢nums[i-1], 会有影响吗.... 我写这句话的时候又发现疑惑消失了
应该根据是否抢nums[i]进行分类
如果抢nums[i], 那么只能从f(i-2)转移到f(i); 疑惑又出现了, 如果实现f(i-1)时并没有抢nums[i-1],那么也能从f(i-1)转移到f(i). 是这样的, 如果实现f(i-1)时没有抢nums[i-1], 说明f(i-1)==f(i-2), 还是相当于从f(i-2)转移到f(i); 如果实现f(i-1)时抢了nums[i-1], 那么只能从f(i-2)转移到f(i); 这是一种数学归纳法的感觉吗?
如果不抢nums[i], 那么就能从f(i-1)转移到f(i)
*/
class Solution {
    public int rob(int[] nums) {
        //input check
        if(nums==null || nums.length==0)
            return 0;
        /*
        f(i)表示[0,...,i]各种可能的最大值
        f(i) = f(i-1), 不抢nums[i]
        f(i) = f(i-2) + nums[i], 抢nums[i]
        当前状态只取决于前两个状态
        */
        if(nums.length==1)
            return nums[0];
        if(nums.length==2)
            return nums[0] > nums[1] ? nums[0] : nums[1];
        int a = nums[0], b = nums[0] > nums[1] ? nums[0] : nums[1], c = 0;
        for(int i=2; i<nums.length; i++){
            //execute
            c = Math.max(b, a+nums[i]);
            //update
            a = b;
            b = c;
        }
        return c;
        
    }
}
接着又写了一遍稍稍简化的版本
class Solution {
    public int rob(int[] nums) {
        if(nums==null || nums.length==0)
            return 0;
        //initialize
        int a = 0, b = nums[0], c = nums[0];
        for(int i=1; i<nums.length; i++){
            //execute
            c = Math.max(a+nums[i], b);
            //update
            a = b;
            b = c;
        }
        return c;
    }   
}
题解; 初始状态比我写的简写许多
class Solution {
    public int rob(int[] nums) {
        int len = nums.length;
        if(len == 0)
            return 0;
        int[] dp = new int[len + 1];
        dp[0] = 0;
        dp[1] = nums[0];
        for(int i = 2; i <= len; i++) {
            dp[i] = Math.max(dp[i-1], dp[i-2] + nums[i-1]);
        }
        return dp[len];
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值