动态规划——打家劫舍(C++)

发布于:2024-07-03 ⋅ 阅读:(14) ⋅ 点赞:(0)

好像,自己读的书确实有点少了。

——2024年7月2日


198. 打家劫舍 - 力扣(LeetCode)

题目描述

        你是一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警。给定一个代表每个房屋存放金额的非负整数数组,计算你 不触动警报装置的情况下 ,一夜之内能够偷窃到的最高金额。

示例 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数组的含义都是自己定的,定好之后就需要思考自己如何实现,列出状态转移方程,题目就迎刃而解了。

 解法一

1. 定义dp数组含义;

        dp[i]表示偷取以 i 结尾的房间能够获取的最大价值。

2. 根据dp含义列出状态转移方程;

        因为小偷不能偷取连续的两个房间,那么如果偷取了第 i 个房间,第 i-1 间房间就不能偷取了,否则会触发警报,但是 i-2 以前的所有房间都是可以偷取的,所以列出状态转移方程如下:
        dp[i] = max( dp[0], dp[1], ..., dp[i - 2] ) + nums[i];

3. 确定边界条件;

        ①如果只有一个房间,那么dp[0] = nums[0];
        ②如果只有两个房间,那么dp[1] = max(nums[0], nums[1]);

4.思考实现方式;

        对于dp[i] = max( dp[0], dp[1], ..., dp[i - 2] ) + nums[i]如何实现呢?

        思考这段方程的含义:这段方程是实现每次偷取第 i 个房间的时候选取前 i-2 个房间中能够获取的最大价值,考虑利用大根堆的优先队列实现。

        因为每次都要选取前 i - 2 个中的最大值,而大根堆的堆顶元素又是整个堆的最大值,可以考虑在 i = 2时开始向优先队列中插入元素,这样就能保证每次取出堆顶元素就是前 i - 2 个值中的最大值。

代码实现
class Solution {
public:
    int rob(vector<int>& nums) {
        // 利用优先队列和动态规划完成 dp[i] = max(dp[0]...dp[i-2]) + nums[i]
        // dp[i]表示偷取以i结尾的房间的最大价值和
        int len = nums.size();
        priority_queue<int> pq;
        vector<int> dp(len, 0);
        dp[0] = nums[0];

        int maxValue = nums[0];
        for(int i = 1; i < len; i++){
            if(i >= 2){
                pq.push(dp[i-2]);
            }
            if(i == 1){
                dp[i] = max(nums[i-1], nums[i]);
            }
            else{
                dp[i] = pq.top() + nums[i];
            }

            maxValue = max(maxValue, dp[i]);
        }
        return maxValue;
    }
};

解法二

1. 定义dp数组含义;

        dp[i]表示偷取前 i 个房间能够获得的最大价值;

2. 根据dp含义列出状态转移方程;

        每个房间都有偷与不偷两种可能,那么在计算dp[i]的时候就会出现两种情况;
        ①偷:dp[i] = dp[i-2] + nums[i];//不能偷连续的两个房间
        ②不偷:dp[i] = dp[i-1];//如果不偷那么当前的最大价值就是dp[i-1]的最大价值。

3. 确定边界条件;

        ①如果只有一个房间,那么dp[0] = nums[0];
        ②如果只有两个房间,那么dp[1] = max(nums[0], nums[1]);

4.思考实现方式;

        这个相对于解法一就很好实现了,利用vector容器即可。

代码实现
class Solution {
public:
    int rob(vector<int>& nums) {
        // dp[i]表示偷取前i的房间的最大价值和
        int len = nums.size();
        vector<int> dp(len, 0);
        dp[0] = nums[0];
        for(int i = 1; i < len; i++){
            if(i == 1){
                dp[i] = max(nums[0], nums[1]);
            }
            else{
                dp[i] = max(dp[i-1], dp[i-2] + nums[i]);
            }
        }
        return dp[len-1];
    }
};

结果展示

解法一 解法二


网站公告

今日签到

点亮在社区的每一天
去签到