简单多状态 dp
题目链接: leetcode.cn/problems/th…
题目描述:
一个有名的按摩师会收到源源不断的预约请求,每个预约都可以选择接或不接。在每次预约服务之间要有休息时间,因此她不能接受相邻的预约。给定一个预约请求序列,替按摩师找到最优的预约集合(总预约时间最长),返回总的分钟数。
注意: 本题相对原题稍作改动
示例 1:
输入: [1,2,3,1] 输出: 4 解释: 选择 1 号预约和 3 号预约,总时长 = 1 + 3 = 4。
示例 2:
输入: [2,7,9,3,1] 输出: 12 解释: 选择 1 号预约、 3 号预约和 5 号预约,总时长 = 2 + 9 + 1 = 12。
示例 3:
输入: [2,1,4,5,3,1,1,3] 输出: 12 解释: 选择 1 号预约、 3 号预约、 5 号预约和 8 号预约,总时长 = 2 + 4 + 3 + 3 = 12。
题目解析
题目意思是让我们替按摩师找到预约总时长最长的方法,并且预约间不能连续。
暴力解法:
暴力解法是两层循环,但我们先要设置一个数组 arr,用来记录 i 下标的最大预约总时长。
第一层循环用来遍历数组,例如现在遍历到 i 下标,第二层循环负责找到 i - 1下标之前(因为不能连续)的最大 arr 值。再更新 arr[i] = Max(arr) + nums [i](当前的预约时间)
下面是另一种解法,多状态动态规划 多状态动态规划: 我们先设置 dp[i] 为当前下标的最大预约时间 但是我们设置完会发现,这个 dp[i] 不是一种状态,它是可能被预约的,也可能不被预约。 所以我们要将 dp 分为多种状态,假设被预约设置为 t[i],不被预约设置为 f[i]。 那么他们的状态转移方程分别怎么表达呢?
当前下标被预约的最大预约值 t[i]:
t[i]是代表 sums[i]会被预约,那么我们就需要 i - 1 之前的最大预约值,那不就是 f[i - 1]嘛, f[i - 1]代表的是当前下标不被预约的最大预约值,所以 sums[i - 1]是不被包含在 f[i - 1]中的,所以 t[i] = f[i - 1]
当前下标不被预约的最大预约值 f[i]:
我们只知道当前下标不被预约,那么 i - 1 下标被预约没我们并不知道,所以 f[i]有两种表达,第一是 sums[i - 1]被预约了,所以 f[i] = t[i - 1]; 第二种是 sums[i - 1]没被预约,此时 f[i] = f[i - 1];我们需要的是最大值,所以最终 f[i] = Max(t[i - 1],f[i - 1])
下面就是细节问题了,我们怎么样初始化 t[i]、f[i],因为 t[i]是被预约,所以 t[0] = sums[0],而 f[i]相反,素以 f[i]默认为 0 就可以了。还有如果传入的数组长度为 0 的时候,我们直接返回 0 就可以了。
代码
暴力解法:
class Solution {
public int massage(int[] nums) {
int[] dp = new int[nums.length];
int result = 0;
for (int i = 0; i < dp.length; i++) {
int max = 0;
for (int j = 0; j < i - 1; j++){
max = Math.max(dp[j],max);
}
dp[i] = max + nums[i];
result = Math.max(result,dp[i]);
}
return result;
}
}
多态动态规划:
class Solution {
public int massage(int[] nums) {
int len = nums.length;
if (len == 0) return 0;
int[] t = new int[len];
int[] f = new int[len];
t[0] = nums[0];
for (int i = 1; i < len; i++) {
t[i] = f[i - 1] + nums[i];
f[i] = Math.max(t[i - 1], f[i - 1]);
}
return Math.max(t[len - 1], f[len - 1]);
}
}