【leetcode】增维来消除后效性--按摩师题解

1.题目描述

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

示例 1:
输入: [1,2,3,1]
输出: 4
解释: 选择 1 号预约和 3 号预约,总时长 = 1 + 3 = 4。

2.题目分析

首先确定题目中要求的是“最优值”,而且并没有要求返回最优解路径,故可以采用动态规划的方法。
我参考了leetcode里的题解,方法可以有两种:

方法一 二维数组

首先以每一天当作一个状态,而一个特定的状态i,他的最优值是受到i-1影响的。那这个最优值我们怎么定义呢?容易看出,每一个状态的可能有两种:选择该序列值,不选择该序列值,我们分别将其定义为dp[i][1],dp[i][0]。
至于为什么采用增维,我觉得liweiwei大佬解释的很让我耳目一新。
由上的分类,我们可以讨论出题目的状态转移方程:
不接受当前预约:dp[i][0]=max(dp[i-1][0],dp[i-1][1])
接受当前预约:dp[i][1]=dp[i-1][0]+nums[i]

其中不接受i预约时,第i-1天的预约可接受也可拒绝,故选择二者之中的最大值。接受当前预约时,第i-1天的预约必然不能接受,故当前最优值就等于dp[i-1][0]与当前序列值nums[i]的和。

讨论出状态转移方程之后,就要考虑初始化,和最终值的问题。
很明显,在第一天的时候,dp[0][0]=0,dp[0][1]=nums[0] ,
注意!:c++在这里动态申请二维数组,如果空出dp[0][0]而直接用dp[1][0]的话会出现执行错误!!!

最终仅需输出dp[n-1][0]和dp[n-1][1]的最大值

代码如下:

class Solution {
public:
    int massage(vector<int>& nums) {
         int n=nums.size();
         if (!n) return 0;
        int **dp=new int*[n];//不要开成n+1而不用0下标!
        for(int i=0;i<n;++i) dp[i]=new int[2];
        dp[0][0]=0;
        dp[0][1]=nums[0];//如果不选择是0,选择的话是1
		for (int i=1;i<n;++i){
			dp[i][0]=max(dp[i-1][1],dp[i-1][0]);
			dp[i][1]=dp[i-1][0]+nums[i];
		} 
		return max(dp[n-1][0],dp[n-1][1]);
    }
};

方法二 一维数组

事实上,上述方法的增维可以直接用一维数组来替代,我们这样考虑:
仍然以dp[i]为第i天的最优值,如果选择i,那么它必然是从i-2转移过来;如果不选择i,那么它必然是从i-1转移过来,故状态转移方程如下:dp[i]=max(dp[i-2]+nums[i],dp[i-1])

3.补充

在上述方法的基础上可以利用滚动数组对解法的空间进行压缩,具体实现(以及方法二的代码)请参考:甜姨的解法

ps:菜鸡的第一篇博客啊!!值得纪念!!(撒花)

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值