198. 打家劫舍

题目链接:

本题考察动态规划。

实现一、递推

\(f[i]\) 表示考虑下标从 \(0 \sim i\) 的房屋最多能抢劫到的金额。
思考状态转移时考虑第 \(i\) 个房屋抢或不抢。
由于不能抢劫相邻的房屋,若抢了 \(i\) 个房屋,则第 \(i-1\) 个房屋就不能抢,再抢只能从 \(i-2\) 开始考虑,即 \(\rm f[i-2]+nums[i]\)。若不抢第 \(i\) 个房屋,则从 \(i-1\) 开始考虑,即 \(\rm f[i-1]\)

class Solution {
public:
    static const int N = 110;
    int f[N];
    int rob(vector<int>& nums) {
        int n = nums.size();
        if (n == 1) return nums[0];
        if (n == 2) return max(nums[0], nums[1]);//由于房屋个数未知,而状态转移时会用到i-2,i从2开始循环迭代,因此需要特判一下~
        f[0] = nums[0];
        f[1] = max(nums[0], nums[1]);
        for (int i = 2; i < n; i++) {
            f[i] = max(f[i - 2] + nums[i], f[i - 1]);
        }
        return f[n - 1];
    }
};

若不想特判,使得代码看起来更加简洁,可以考虑把 \(f\) 数组的每个下标值都加上 \(2\)

class Solution {
public:
    static const int N = 110;
    int f[N];
    int rob(vector<int>& nums) {
        int n = nums.size();
        for (int i = 0; i < n; i++) {
            f[i + 2] = max(f[i + 1], f[i] + nums[i]);
        }
        return f[n + 1];
    }
};

实现二、记忆化搜索

class Solution {
public:
    int rob(vector<int>& nums) {
        int n = nums.size();
        vector<int> memo(n, -1);//-1表示没有计算过
		
	//dfs(i)表示从nums[0]到nums[i]最多能偷多少
        function<int(int)> dfs = [&] (int i) -> int {
            if (i < 0) return 0;//递归边界(没有房子)
            if (memo[i] != -1) return memo[i];//之前计算过
            return memo[i] = max(dfs(i - 1), dfs(i - 2) + nums[i]);
        };
        return dfs(n - 1);//一直到最后一个房子
    }
};

补充:空间优化版(滚动数组

观察状态转移方程不难发现,每次都只是用到了上一个状态和上上个状态。因此我们不妨用变量分别记录这两个状态,计算完新状态后就更新这两个变量的值,可将空间复杂度优化到 \(O(1)\)

class Solution {
public:
    int rob(vector<int> &nums) {
        int f0 = 0, f1 = 0;
        for (int x : nums) {
            int new_f = max(f1, f0 + x);
            f0 = f1;
            f1 = new_f;
        }
        return f1;
    }
};
posted @ 2024-04-09 22:09  胖柚の工作室  阅读(12)  评论(0)    收藏  举报