题目
你是一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统。如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警。
给定一个代表每个房屋存放金额的非负整数数组,在不触动警报装置的情况下 ,计算你一夜之内能够偷窃到的最高金额。
示例 1:
输入:[1,2,3,1]输出:4解释:偷窃1号房屋 (金额 = 1) ,然后偷窃3号房屋 (金额 = 3),偷窃到的最高金额为1 + 3 = 4。
示例 2:
输入:[2,7,9,3,1]输出:12解释:偷窃1号房屋 (金额 = 2), 偷窃3号房屋 (金额 = 9),接着偷窃5号房屋 (金额 = 1),偷窃到的最高金额为2 + 9 + 1 = 12。
解析
这道题主要考察应聘者对动态规划算法的理解和应用能力,在面试中比较常见。
首先,我们要明确问题的限制条件和目标。在这个问题中,小偷不能偷相邻的房屋,因为相邻的房屋装有连通的防盗系统,如果两间相邻的房屋在同一晚上被闯入,系统会自动报警。我们的目标是找出小偷在不触发警报的情况下,一夜之内能够偷到的最高金额。
由于小偷不能偷相邻的房屋,这意味着在偷窃过程中,我们需要做出选择:偷这家还是那家。这种类型的问题非常适合用动态规划(DP)来解决,因为DP能够很好地处理这种需要做出一系列决策以优化最终结果的问题。
在动态规划中,我们需要定义一个状态来表示问题的子问题。在这个问题中,我们可以定义一个DP数组dp,其中dp[i]表示偷到第i家时能够获得的最高金额。注意,这里的“偷到第i家”,并不意味着一定要偷第i家,而是考虑到第i家时的情况。
接下来,我们需要找出状态之间的转移关系。对于第i家,小偷有两种选择:
1、偷第i家:如果小偷选择偷第i家,那么他就不能偷第i-1家。因此,偷到第i家时的最高金额就是偷到第i-2家的最高金额加上第i家的金额,即dp[i] = dp[i-2] + nums[i]。
2、不偷第i家:如果小偷选择不偷第i家,那么偷到第i家的最高金额就是偷到第i-1家的最高金额,即dp[i] = dp[i-1]。
由于小偷想要最大化偷窃的金额,故他会选择这两种情况中的较大值。因此,状态转移方程为:
dp[i] = max(dp[i-1], dp[i-2] + nums[i])
对于动态规划问题,我们还需要考虑边界条件。在这个问题中,有两个明显的边界条件:
当只有一家时(n = 1),小偷只能偷这一家,所以dp[0] = nums[0]。
当有两家时(n = 2),小偷会选择金额较大的那一家偷,所以dp[1] = max(nums[0], nums[1])。
最后,当所有的dp[i]都计算出来后,dp[n-1]就是小偷能够偷到的最高金额,其中n是房屋的数量。具体的实现,可参考下面的示例代码。
pub fn rob(nums: Vec<i32>) -> i32 { let n = nums.len(); if n == 0 { return 0; } else if n == 1 { return nums[0]; } let mut dp = vec![0; n]; dp[0] = nums[0]; dp[1] = std::cmp::max(nums[0], nums[1]); for i in 2..n { dp[i] = std::cmp::max(dp[i - 1], dp[i - 2] + nums[i]); } dp[n - 1]}fn main() { let mut max_amount = rob(vec![1, 2, 3, 1]); println!("{}", max_amount); max_amount = rob(vec![2, 7, 9, 3, 1]); println!("{}", max_amount);}
总结
本题考察了动态规划的应用,需要合理地定义状态,找出状态转移方程,并正确处理边界条件。通过这道题,可以加深我们对动态规划算法的理解和运用。