《算法题讲解指南:动态规划算法--路径问题》--11.按摩师,12.打家劫舍II

🔥小叶-duck个人主页

❄️个人专栏《Data-Structure-Learning》《C++入门到进阶&自我学习过程记录》
《算法题讲解指南》--优选算法
《算法题讲解指南》--递归、搜索与回溯算法
《算法题讲解指南》--动态规划算法

未择之路,不须回头
已择之路,纵是荆棘遍野,亦作花海遨游


目录

11.按摩师

题目链接:

题目描述:

题目示例:

解法(动态规划):

算法思路:

C++算法代码:

算法总结及流程解析:

前置任务:打家劫舍

题目链接:

题目描述:

题目示例:

C++算法代码:

12.打家劫舍II

题目链接:

题目描述:

题目示例:

解法(动态规划):

算法思路:

C++算法代码:

算法总结及流程解析:

结束语


11.按摩师

题目链接:

面试题 17.16. 按摩师 - 力扣(LeetCode)

题目描述:

题目示例:

解法(动态规划):

算法思路:

1.状态表示:

对于简单的线性dp,我们可以用「经验 +题目要求」来定义状态表示:

i.以某个位置为结尾,巴拉巴拉;

ii.以某个位置为起点,巴拉巴拉。

这里我们选择比较常用的方式,以某个位置为结尾,结合题目要求,定义一个状态表示:

dp[i]表示:选择到i位置时,此时的最长预约时长。

但是我们这个题在i位置的时候,会面临「选择」或者「不选择」两种抉择,所依赖的状态需要细分:

f[i]表示:选择到 i位置时, nums[i]必选,此时的最长预约时长;

g[i]表示:选择到i 位置时,nums[i]不选,此时的最长预约时长。

2.状态转移方程:

因为状态表示定义了两个,因此我们的状态转移方程也要分析两个:

对于f[i]:如果nums[i]必选,那么我们仅需知道i-1位置在不选的情况下的最长预约时长,然后加上nums[i]即可,因此f[i]=g[i-1] + nums[i]。

对于g[i]:如果nums[i]不选,那么i-1位置上选或者不选都可以。因此,我们需要知道 i -1 位置上选或者不选两种情况下的最长时长,因此 g[i]= max(f[ i - 1],g[ i - 1]) 。

3.初始化:

这道题的初始化比较简单,因此无需加辅助节点,仅需初始化f[0]=nums[0],g[0]=0即可。

4.填表顺序:

根据「状态转移方程」得「从左往右,两个表一起填」。

5.返回值:

根据「状态表示」,应该返回 max(f[n - 1], g[n - 1]) 。

C++算法代码:

cpp 复制代码
class Solution {
public:
    int massage(vector<int>& nums) 
    {
        if(nums.size() == 0)
        {
            return 0;
        }
        //vector<int> dp(nums.size());
        vector<int> f(nums.size());
        vector<int> g(nums.size());
        //dp[i]表示第i个位置所预约的最长时间,通过f和g来表示,可以不创建
        //f[i]表示第i个位置选择预约时的预约最长时间;
        //g[i]表示第i个位置不选择预约时的预约最长时间
        f[0] = nums[0]; //g[0] = 0,创建数组时g[0]就是0
        for(int i = 1; i < nums.size(); i++)
        {
            f[i] = g[i - 1] + nums[i];
            g[i]= max(f[i - 1], g[i - 1]);
            //dp[i] = max(f[i], g[i]);
            //由于f[i]不知道,所以只能由前一个位置得到:
            //因为f[i]表示第i个位置选择预约,则i-1位置一定不选择预约->g[i-1]
            //所以f[i] = g[i - 1] + nums[i]

            //由于g[i]不知道,所以只能由前一个位置得到:
            //因为g[i]表示第i个位置不选择预约,则i-1位置又会有两种情况:
            //如果第i-1位置选择预约->f[i-1],则g[i]=f[i-1]
            //如果第i-1位置不选择预约->g[i-1],则g[i]=g[i-1]
            //所以我们要得到预约的最长时间->g[i]=max(f[i - 1], g[i - 1])
        }
        //return dp[nums.size() - 1];
        return max(f[nums.size() - 1], g[nums.size() - 1]);
    }
};

算法总结及流程解析:

前置任务:打家劫舍

题目链接:

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

题目描述:

题目示例:

C++算法代码:

cpp 复制代码
class Solution {
public:
    int rob(vector<int>& nums) 
    {
        int n = nums.size();
        vector<int> f(n);//f[i]表示第i个位置选择偷时,当前偷到的最大金额
        vector<int> g(n);//g[i]表示第i个位置选择不偷,当前偷到的最大金额
        f[0] = nums[0];
        for(int i = 1; i < n; i++)
        {
            f[i] = g[i - 1] + nums[i];
            g[i] = max(f[i - 1], g[i - 1]);
        }
        return max(f[n - 1], g[n - 1]);
    }
};

12.打家劫舍II

题目链接:

213. 打家劫舍 II - 力扣(LeetCode)

题目描述:

题目示例:

解法(动态规划):

算法思路:

这一个问题是「打家劫舍I」问题的变形。

上一个问题是一个「单排」的模式,这一个问题是一个「环形」的模式,也就是首尾是相连的。但是我们可以将「环形」问题转化为「两个单排」问题:

a.偷第一个房屋时的最大金额x ,此时不能偷最后一个房子,因此就是偷[0,n -2]区间的房子;

b.不偷第一个房屋时的最大金额 y ,此时可以偷最后一个房子,因此就是偷[1,n-1]区

间的房子;

两种情况下的「最大值」,就是最终的结果。

因此,问题就转化成求「两次单排结果的最大值」。

C++算法代码:

cpp 复制代码
class Solution {
public:
    //环形打家劫舍
    int rob(vector<int>& nums) 
    {
        int n = nums.size();
        return max(nums[0] + Rob(nums, 2, n - 2), Rob(nums, 1, n - 1));   
        //当选择偷第0个位置,则第1个位置和n-1位置都不能偷
        //则(2, n - 2)区间满足线性打家劫舍的逻辑
        //当不选择偷第0个位置,则(1, n - 1)区间满足线性打家劫舍的逻辑
    }

    //线性打家劫舍
    int Rob(vector<int>& nums, int left, int right) 
    {
        if(left > right)
        {
            return 0;
        }
        int n = right - left + 1;
        vector<int> f(n);//f[i]表示第i个位置选择偷时,当前偷到的最大金额
        vector<int> g(n);//g[i]表示第i个位置选择不偷,当前偷到的最大金额
        f[0] = nums[left]; //一定要注意nums下标的映射关系
        for(int i = 1; i < n; i++)
        {
            f[i] = g[i - 1] + nums[i + left];//这里也是要注意nums的对应位置
            g[i] = max(f[i - 1], g[i - 1]);
        }
        return max(f[n - 1], g[n - 1]);
    }
};

算法总结及流程解析:

结束语

到此,11.按摩师,12.打家劫舍II 这两道算法题就讲解完了。**按摩师问题:通过定义f[i](选择i位置)和g[i](不选i位置)两个状态表示,建立状态转移方程并初始化,最终返回最大值;打家劫舍II:则将环形结构转化为两个线性问题:偷第一个房屋(不偷最后一个)和不偷第一个房屋(可偷最后一个),分别求解后取最大值。**希望大家能有所收获!

相关推荐
charlie11451419120 小时前
嵌入式C++教程实战之Linux下的单片机编程(9):HAL时钟使能 —— 不开时钟,外设就是一坨睡死的硅
linux·开发语言·c++·单片机·嵌入式硬件·c
放羊郎20 小时前
机器人跟随算法
算法·机器人
liu****20 小时前
第十五届蓝桥杯大赛软件赛国赛C/C++大学B组
c++·算法·蓝桥杯·acm
zhooyu20 小时前
利用叉乘判断OpenGL中的左右关系
c++·3d·opengl
We་ct20 小时前
LeetCode 172. 阶乘后的零:从暴力到最优,拆解解题核心
开发语言·前端·javascript·算法·leetcode·typescript
轻微的风格艾丝凡20 小时前
三相不平衡电流调试经验记录
算法·dsp
老虎062720 小时前
LeetCode热题100 刷题笔记(第五天)双指针法 「 三数之和 」
笔记·算法·leetcode
光电笑映20 小时前
C++11 新特性全解:语法糖、容器进化与可调用对象包装
开发语言·c++
汀、人工智能20 小时前
[特殊字符] 第97课:前K个高频元素
数据结构·算法·数据库架构··数据流·前k个高频元素
沉鱼.4420 小时前
第十四届题目
数据结构·算法