首页 > 技术文章 > leetcode198

asenyang 2017-04-20 16:09 原文

public class Solution {
    public int Rob(int[] nums) {
        int i = 0;
            int e = 0;
            for (int k = 0; k < nums.Length; k++)
            {
                int tmp = i;
                i = nums[k] + e;
                e = Math.Max(tmp, e);
            }
            return Math.Max(i, e);
    }
}

https://leetcode.com/problems/house-robber/#/description

/*
你是一个专业强盗,并计划沿街去盗窃每一个住户。
每个房子都有一定量的现金,阻止你盗窃的唯一阻碍是相邻的两个房子之间有安全系统。
一旦这两个房子同时被盗窃,系统就会自动联系警察。
给定一系列非负整数代表每个房子的金钱,
求出在不惊动警察的情况下能盗窃到的最大值*/

上面的程序不是很容易理解,略微进行修改如下:

public class Solution
    {
        public int Rob(int[] nums)
        {
            if (nums.Length > 0)
            {
                int i = nums[0];
                int e = 0;
                for (int k = 1; k < nums.Length; k++)
                {
                    var tmp = nums[k] + e;//抢当前的房间的累积金额,临时存储
                    e = Math.Max(i, e);
                    //i:不抢当前房间但是抢前一个房间   
                    //e:不抢当前房间同时不抢前一个房间
                    //两者大的是新的e:不抢当前房间累积金额
                    i = tmp;//抢当前房间的累积金额
                }
                return Math.Max(i, e);
            }
            else
            {
                return 0;
            }
        }
    }

 经过一段时间学习,重新做这道题,使用了更加容易理解的写法:

public class Solution
    {
        public int Rob(int[] nums)
        {
            var len = nums.Length;
            if (len == 0)
            {
                return 0;
            }
            else if (len == 1)
            {
                return nums[0];
            }
            else if (len == 2)
            {
                return Math.Max(nums[0], nums[1]);
            }
            //len>=3
            //var robmoney = 0;//累计的抢夺的钱
            var money = new int[len];//记录截止到当前位置最多的金额
            money[0] = nums[0];
            money[1] = Math.Max(nums[0], nums[1]);
            for (int i = 2; i < len; i++)
            {
                //如果当前房间-1已经被抢了,那么当前房间不能抢 新的累计金额是之前最大金额

                //如果当前房间-1没有被抢,则新的累计金额是 之前最大金额+当前房间金额
                money[i] = Math.Max(money[i - 1], money[i - 2] + nums[i]);
                //robmoney += money[i];
            }

            return money[len-1];
        }
    }

用money数组记录,到当前i位置为止,所抢夺的最大的金额。

决定当前的i位置是否要抢里面的钱,根据i-1房间是否已经抢过来判断。

如果i-1房间被抢,那i位置房间的金额就不可以再抢。如果i-1房间没有被抢,则i位置最大金额就是i-2的最大金额+i房间的金额。

每次记录的是,截止到目前位置,最大的金额数。也就是这两种方案中较大的一种。

 

补充一个python的版本:

 1 class Solution:
 2     def rob(self, nums: List[int]) -> int:
 3         n = len(nums)
 4         if n == 0:
 5             return 0
 6         elif n == 1:
 7             return nums[0]
 8         elif n == 2:
 9             return max(nums[0],nums[1])
10         
11         dp = [0] * (n + 1)
12         dp[0] = 0
13         dp[1] = nums[0]
14         for i in range(2,n+1):
15             dp[i] = max(dp[i-2]+nums[i-1],dp[i-1])
16         return dp[n]

定义dp,长度n + 1,表示“到当前房间为止,所获得的最多钱数”。

dp[0]初始化为0,方便计算。

最后返回dp[n]为所求。

关键的公式是第15行,表示:当前房间能获得的最多钱数,是两种策略选择其一:

策略1:抢夺当前房间的钱,则抢夺后获得的金钱数量为,跳过前1个房间,也就是“上上个”房间所获得的最多金钱 + 当前房间的金钱

策略2:不抢夺当前房间的钱,则抢夺后(实际上没有抢)的金钱和“上个”房间所获得的最多金钱值一样。

比较这两种策略,选择多的作为dp[i]的结果,即表示截止到当前房间,所获得的最多的金钱数量。

推荐阅读