《算法题讲解指南:动态规划算法--路径问题》--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:则将环形结构转化为两个线性问题:偷第一个房屋(不偷最后一个)和不偷第一个房屋(可偷最后一个),分别求解后取最大值。**希望大家能有所收获!

相关推荐
历程里程碑2 小时前
43. TCP -2实现英文查中文功能
java·linux·开发语言·c++·udp·c#·排序算法
代码探秘者2 小时前
【算法篇】1.双指针
java·数据结构·人工智能·后端·python·算法
qq_417695052 小时前
C++中的中介者模式
开发语言·c++·算法
xiangpanf2 小时前
PHP爬虫框架:Goutte vs Panther
开发语言·c++·vue.js·php
像素猎人2 小时前
pair<类型1, 类型2> 变量名的介绍,自用笔记
开发语言·c++·算法
向往着的青绿色2 小时前
完全平方数【Letcode279题解】
开发语言·c++·数学·算法·面试·性能优化·动态规划
赵民勇2 小时前
gtkmm之耗时操作不阻塞界面
linux·c++
手握风云-2 小时前
优选算法的后进之道:栈专题
算法
xwz小王子2 小时前
Science Robotics 赋予机器人“类脑”触觉,低成本视觉-触觉预训练攻克灵巧手多任务操作
人工智能·算法·机器人