60题学会动态规划系列:动态规划算法第三讲

这篇具有很好参考价值的文章主要介绍了60题学会动态规划系列:动态规划算法第三讲。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

简单多状态问题

文章目录

  • 一.按摩师
  • 二.打家劫舍系列
  • 三.删除并获得点数
  • 四.粉刷房子

1.按摩师

力扣链接:力扣

一个有名的按摩师会收到源源不断的预约请求,每个预约都可以选择接或不接。在每次预约服务之间要有休息时间,因此她不能接受相邻的预约。给定一个预约请求序列,替按摩师找到最优的预约集合(总预约时间最长),返回总的分钟数。

60题学会动态规划系列:动态规划算法第三讲

首先我们分析一下题目:1.每个预约可以选择接或者不接 2.不能接受相邻的预约。

1.状态表示

我们用dp[i]表示i位置的最长分钟数,而i位置又可以分为两种情况,一种是接,一种是不接,所以我们用两个dp表来表示状态。

f[i]表示接i位置的最长分钟数

g[i]表示不接i位置的最长分钟数 

2.状态转移方程

因为f[i]表示接i位置的预约,由于题目要求不能接受相邻的预约,所以我们一定不能接受i-1位置的预约,而g[i-1]就表示不接i-1位置的最长分钟数,又因为我们接了i位置要加上i位置的分钟数,所以

f[i] = g[i-1] + nums[i];

g[i]表示不接i位置,那么我们可以选择接受i-1位置或者不接受i-1位置,又因为我们要的是最大值,所以取这两种情况的较大值即可。

g[i] = max(f[i-1],g[i-1])

3.初始化

从状态转移方程来看,我们造成越界的位置只有f[0]和g[0],而f[0]表示接受0位置的最长分钟数,那么f[0] = nums[0]     g[0] = 0.

4.填表

已知f[0]和g[0]所以从左向右依次填表,两个表一起填

5.返回值

因为预约时长都是正数,所以越到后面时间越长,那么最长的只有两种情况:最后一个位置接或者最后一个位置不接,此题要求最大值,所以是这两个情况的较大值。

class Solution {
public:
    int massage(vector<int>& nums) {
       if (nums.size()==0) return 0;
       //创建dp表
       int n = nums.size();
       vector<int> f(n,0),g(n,0);
       f[0] = nums[0],g[0] = 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]);
    }
};

 注意:此题会有nums.size()为0的情况,所以需要单独判断。

2.打家劫舍系列

1.打家劫舍I

力扣链接:力扣

你是一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警

给定一个代表每个房屋存放金额的非负整数数组,计算你 不触动警报装置的情况下 ,一夜之内能够偷窃到的最高金额。

60题学会动态规划系列:动态规划算法第三讲

 1.状态表示

由于每个位置存在偷与不偷两个状态,所以需要两个表来表示。

f[i]表示偷i位置的最高金额

g[i]表示不偷i位置的最高金额

2.状态转移方程

f[i] = g[i-1] + nums[i];

g[i] = max(f[i-1],g[i-1])

3.初始化,4.填表,5.返回值

与第一题同理。

对于这个题我们应该很熟悉吧,没错!和第一题按摩师简直一模一样,我们直接把按摩师的代码复制过来:

60题学会动态规划系列:动态规划算法第三讲

 相同的代码,立马就搞定了,我们将这道题拿出来是为了引出打家劫舍II和III,所以并不是没有用哦~

2.打家劫舍II

力扣链接:力扣

你是一个专业的小偷,计划偷窃沿街的房屋,每间房内都藏有一定的现金。这个地方所有的房屋都 围成一圈 ,这意味着第一个房屋和最后一个房屋是紧挨着的。同时,相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警 。

给定一个代表每个房屋存放金额的非负整数数组,计算你 在不触动警报装置的情况下 ,今晚能够偷窃到的最高金额。

注意:和I不同的是,这道题的前后是一个圈,也就是说这是一个环形问题,下面我们来解决这道题。

首先我们能将问题分为两种情况,第一种是偷第一家的情况,第二种是不偷第一家的情况。

如果偷第一家,那么第二家和最后一家不能偷,也就是说我们只需要对2到n-2这个范围做一次打家劫舍I,最后加上nums【0】即可。

如果不偷第一家,那么1到n-1这个范围内可以随便偷,所以对1到n-1这个范围内做一次打家劫舍I即可。

60题学会动态规划系列:动态规划算法第三讲

class Solution {
public:
    int rob(vector<int>& nums) {
       int n = nums.size();
       return max(nums[0]+rob1(nums,2,n-2),rob1(nums,1,n-1));
    }
    int rob1(vector<int>& nums,int left,int right)
    {
        if (left>right) return 0;
        int n = nums.size();
        vector<int> f(n, 0), g(n, 0);
        f[left] = nums[left], g[left] = 0;
        for (int i = left+1; i <= right; i++)
        {
            f[i] = g[i - 1] + nums[i];
            g[i] = max(f[i - 1], g[i - 1]);
        }
        return max(f[right], g[right]);
    }
};

 注意:当我们left大于right的时候那么一定是越界的情况,这种情况返回0即可。当我们将区间定为left到right这个闭区间,那么就从left+1开始填表,填到right,最后返回的时候也是返回dp表中right的位置,这里一定要画图映射位置关系。

3.删除并获得点数

力扣链接:力扣

给你一个整数数组 nums ,你可以对它进行一些操作。

每次操作中,选择任意一个 nums[i] ,删除它并获得 nums[i] 的点数。之后,你必须删除 所有 等于 nums[i] - 1 和 nums[i] + 1 的元素。

开始你拥有 0 个点数。返回你能通过这些操作获得的最大点数。

60题学会动态规划系列:动态规划算法第三讲

 首先通过题目我们可以看到当我们删除X获取X的点数后,必须删除X-1和X+1的元素,我们发现这与打家劫舍的问题非常像,都是不能获得相邻元素的值,那么我们该如何将这个问题转化为打家劫舍问题呢?其实很简单,因为X-1 和 X 和 X+1这三个数都是相邻的,我们直接将nums映射到一个与下标同值的表中,比如3就放在下标为3的位置,并且给这个位置加上3,然后我们对这个新数组做一次打家劫舍就得到了最大点数。

比如实例2,映射后变成如下图:

60题学会动态规划系列:动态规划算法第三讲

然后我们对这个数组做一次打家劫舍,获取的最大点数就是9.

class Solution {
public:
    int deleteAndEarn(vector<int>& nums) {
        int hash[10001] = {0};
        const int n = 10001;
        for (auto& a:nums)
        {
            hash[a]+=a;
        }
        vector<int> f(n, 0), g(n, 0);
        for (int i = 1; i < n; i++)
        {
            f[i] = g[i - 1] + hash[i];
            g[i] = max(f[i - 1], g[i - 1]);
        }
        return max(f[n-1], g[n-1]);
    }
};

 此方法就是用空间换时间。

4.粉刷房子

力扣链接:力扣

假如有一排房子,共 n 个,每个房子可以被粉刷成红色、蓝色或者绿色这三种颜色中的一种,你需要粉刷所有的房子并且使其相邻的两个房子颜色不能相同。

当然,因为市场上不同颜色油漆的价格不同,所以房子粉刷成不同颜色的花费成本也是不同的。每个房子粉刷成不同颜色的花费是以一个 n x 3 的正整数矩阵 costs 来表示的。

例如,costs[0][0] 表示第 0 号房子粉刷成红色的成本花费;costs[1][2] 表示第 1 号房子粉刷成绿色的花费,以此类推。

请计算出粉刷完所有房子最少的花费成本。

60题学会动态规划系列:动态规划算法第三讲

 首先我们通过题目可以知道限制条件为:相邻房屋不能涂成同一个颜色,下面我们开始解题。

1.状态表示

dp【i】表示第i个房子所花费的最低成本,由于每个房子都可以涂成三种颜色,所以dp[i]又可以划分成3个状态。

dp[i][0]表示第i个房子涂成红色的最低成本

dp[i][1]表示第i个房子涂成蓝色的最低成本

dp[i][2]表示第i个房子涂成绿色的最低成本

2.状态转移方程

dp[i][0] = min(dp[i-1][1],dp[i-1][2]) + costs[i][0];

dp[i][1] = min(dp[i-1][0],dp[i-1][2]) + costs[i][1];

dp[i][2] = min(dp[i-1][0],dp[i-1][1]) + costs[i][2];

由于每个房子不能涂成相同颜色,所以第i层的房子如果是红色,那么第i-1层就只能是蓝色和绿色。

3.初始化

由于第一个房子不受前面任何房子的影响(i-1越界),所以第一个房子的三个状态都是已知的

dp[0][0] = costs[0][0],

dp[0][1] = costs[0][1],

dp[0][2] = costs[0][2];

4.填表

从左向右依次填表,三个表一起填

5.返回值

返回最后一个房子的三种状态的最小值。

class Solution {
public:
    int minCost(vector<vector<int>>& costs) {
       int n = costs.size();
       vector<vector<int>> dp(n,vector<int>(3));
       dp[0][0] = costs[0][0],dp[0][1] = costs[0][1],dp[0][2] = costs[0][2];
       for (int i = 1;i<n;i++)
       {
           dp[i][0] = min(dp[i-1][1],dp[i-1][2]) + costs[i][0];
           dp[i][1] = min(dp[i-1][0],dp[i-1][2]) + costs[i][1];
           dp[i][2] = min(dp[i-1][0],dp[i-1][1]) + costs[i][2];
       }
       int ret = dp[n-1][0];
       for (int i =1;i<3;i++)
       {
           if (dp[n-1][i]<ret)
           {
               ret = dp[n-1][i];
           }
       }
       return ret;
    }
};

当然我们也可以将最后取最小值的代码简化一下:

class Solution {
public:
    int minCost(vector<vector<int>>& costs) {
       int n = costs.size();
       vector<vector<int>> dp(n,vector<int>(3));
       dp[0][0] = costs[0][0],dp[0][1] = costs[0][1],dp[0][2] = costs[0][2];
       for (int i = 1;i<n;i++)
       {
           dp[i][0] = min(dp[i-1][1],dp[i-1][2]) + costs[i][0];
           dp[i][1] = min(dp[i-1][0],dp[i-1][2]) + costs[i][1];
           dp[i][2] = min(dp[i-1][0],dp[i-1][1]) + costs[i][2];
       }
       int ret = min(dp[n-1][0],min(dp[n-1][1],dp[n-1][2]));
       return ret;
    }
};

以上就是本篇文章的5道动态规划习题,还是建议大家在做动态规划习题的时候要多画图。文章来源地址https://www.toymoban.com/news/detail-480776.html

到了这里,关于60题学会动态规划系列:动态规划算法第三讲的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【算法学习】简单多状态-动态规划

            本篇博客记录动态规划中的简单多状态问题。         在之前的动态规划类型的题中,我们每次分析的都只是一种或者某一类的状态,定义的dp表也是围绕着一种状态来的。         现在可能对于一种状态,存在几种不同的子状态,在状态转移过程中相互影响。此时

    2024年01月18日
    浏览(40)
  • 算法沉淀 —— 动态规划篇(简单多状态dp问题下)

    几乎所有的动态规划问题大致可分为以下5个步骤,后续所有问题分析都将基于此 1.、状态表示:通常状态表示分为基本分为以下两种,其中更是以第一种为甚。 以i为结尾 ,dp[i] 表示什么,通常为代求问题(具体依题目而定) 以i为开始 ,dp[i]表示什么,通常为代求问题(具

    2024年04月16日
    浏览(46)
  • 【算法优选】 动态规划之简单多状态dp问题——贰

    动态规划相关题目都可以参考以下五个步骤进行解答: 状态表示 状态转移⽅程 初始化 填表顺序 返回值 后面题的解答思路也将按照这五个步骤进行讲解。 给定一个整数数组prices,其中第 prices[i] 表示第 i 天的股票价格 设计一个算法计算出最大利润。在满足以下约束条件下,

    2024年04月12日
    浏览(35)
  • 算法沉淀 —— 动态规划篇(简单多状态dp问题上)

    几乎所有的动态规划问题大致可分为以下5个步骤,后续所有问题分析都将基于此 1.、状态表示:通常状态表示分为以下两种,其中更是第一种为主。 以i为结尾 ,dp[i] 表示什么,通常为代求问题(具体依题目而定) 以i为开始 ,dp[i]表示什么,通常为代求问题(具体依题目而

    2024年04月11日
    浏览(43)
  • 动态规划之简单多状态

    1.题目链接:按摩师 2.题目描述:一个有名的按摩师会收到源源不断的预约请求,每个预约都可以选择接或不接。在每次预约服务之间要有休息时间,因此她不能接受相邻的预约。给定一个预约请求序列,替按摩师找到最优的预约集合(总预约时间最长),返回总的分钟数。

    2024年02月09日
    浏览(41)
  • 【动态规划】简单多状态

    题目链接 状态表示 dp[i] 表示到i位置的时候预约的最大时长。但是这个题目我们可以选择接或不接。因此可以继续划分为两个子状态: f[i] 表示:到i位置时接受的最大时长 g[i] 表示:到i位置时不接受的最大时长 状态转移方程 初始化 因为这个题目比较简单,所以不需要使用

    2024年02月15日
    浏览(50)
  • c++--简单多状态动态规划问题

    PS:以下代码均为C++实现 1.按摩师  力扣 一个有名的按摩师会收到源源不断的预约请求,每个预约都可以选择接或不接。在每次预约服务之间要有休息时间,因此她不能接受相邻的预约。给定一个预约请求序列,替按摩师找到最优的预约集合(总预约时间最长),返回总的分钟

    2024年02月14日
    浏览(53)
  • 「动态规划」简单多状态dp问题

    以经典问题“打家劫舍”来解释简单多状态dp问题和解决方法 题目链接:打家劫舍I 这种问题就是在某一个位置有多个状态可以选择,选择 不同的状态 会影响 最终结果 在这道题中就是小偷在每一个房屋,可以选择偷或不偷,每一次选择都会影响最终偷窃金额 状态表示 因为

    2024年03月15日
    浏览(56)
  • 【动态规划】简单多状态dp问题(2)买卖股票问题

    买卖股票问题 传送门:力扣309. 最佳买卖股票时机含冷冻期 题目: 1.1 题目解析 越难的dp问题,看示例只能起到了解题目的效果,一般推不出啥普遍的规律,所以接下来就是我们的算法原理,通过动归的思想去理解,才会豁然开朗! 1.2 算法原理 1.2.1 状态表示 我们需要通过经

    2024年02月12日
    浏览(56)
  • 算法提高-动态规划-状态机模型

    这题比较简单,主要是学习一下状态机的模版(如何定义状态,dp方程如何推导)。 再学一个知识点:线性dp(i由i-1递推过来)可以用滚动数组优化空间复杂度 限制购买天数 这里也是线性dp,当然可以用滚动数组优化,但是之前大盗阿福写过了,这里就朴素版本了 限制了卖

    2024年02月15日
    浏览(62)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包