两道入门级别的一维DP问题,与昨天的单词划分应该放在一天的
题型:DP、子问题处理
链接:70. 爬楼梯 - 力扣(LeetCode) 198. 打家劫舍 - 力扣(LeetCode)
来源:LeetCode
爬楼梯
题目描述
假设你正在爬楼梯。需要 n
阶你才能到达楼顶。
每次你可以爬 1
或 2
个台阶。你有多少种不同的方法可以爬到楼顶呢?
题目样例
示例 1:
输入:n = 2 输出:2 解释:有两种方法可以爬到楼顶。 1. 1 阶 + 1 阶 2. 2 阶
示例 2:
输入:n = 3 输出:3 解释:有三种方法可以爬到楼顶。 1. 1 阶 + 1 阶 + 1 阶 2. 1 阶 + 2 阶 3. 2 阶 + 1 阶 提示:
1 <= n <= 45
题目思路
一维dp入门问题,重点是对dp数组要理解
大问题是爬 n 楼,有几种方法,那么可以用dp[n]表示爬 n 楼用到的方法数
然后开始处理子问题:i 从 0 到 n ,处理dp[i].
怎么才能爬到 i 层?看题目描述,可以是dp[i-1]的情况下爬1层 或者是 dp[i-2]的情况下爬2层
那么可以得到:dp[i] = dp[i-1] + dp[i-2] 的递归式
然后确定数组元素的初始化:因为要遍历到dp[n],所以dp.size() 是n+1 。dp[0]按照前面定义的dp数组的含义,就相当于“爬0层”的方法——那么定义为dp[0] = 1即可。dp[1] 只能通过dp[0]到达,那么dp[1] = dp[0]。
至此,初始化完毕
C++代码
class Solution {
public:
int climbStairs(int n) {
// n表示楼顶
// 类似斐波那契的一道题
int dp[n+1];//需要遍历到dp[n]
dp[0] = 1,dp[1]=1;
// 爬楼梯的情况:①一次爬 1 层 ② 一次爬 2 层i
// 即 dp[i] = dp[i-1] + dp[i-2]
for(int i=2;i<n+1;i++)
{
dp[i] = dp[i-1] +dp[i-2];
}
return dp[n];
}
};
结算页面
打家劫舍
题目描述
你是一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警。(偷了一家的话,后面相连的那家就不能偷了)
给定一个代表每个房屋存放金额的非负整数数组,计算你 不触动警报装置的情况下 ,一夜之内能够偷窃到的最高金额。
题目样例
示例 1:
输入:[1,2,3,1] 输出:4 解释:偷窃 1 号房屋 (金额 = 1) ,然后偷窃 3 号房屋 (金额 = 3)。 偷窃到的最高金额 = 1 + 3 = 4 。
示例 2:
输入:[2,7,9,3,1] 输出:12 解释:偷窃 1 号房屋 (金额 = 2), 偷窃 3 号房屋 (金额 = 9),接着偷窃 5 号房屋 (金额 = 1)。 偷窃到的最高金额 = 2 + 9 + 1 = 12 。
提示:
1 <= nums.length <= 100
0 <= nums[i] <= 400
题目思路
这边先说一下为何这道题dp数组的容量是n:与爬楼梯对比一下,爬楼梯问题的dp数组是‘爬 k 层用到的方法’,最后要返回的是dp[n] 即爬n层会用到的方法;“小偷问题”dp[k]是“在index为k的家门口,能到手的最多的钱。”门牌号(姑且这样叫)和dp数组的index相同就不需要建立新的指针来遍历dp数组。(创dp[n+1]也可以,但没必要)
“小偷问题”——贪心味道的dp(但dp的题目好多是限制了k条件,求max / min)
那么开始按部就班:创建dp数组dp[n]: 其中dp[n-1] 表示“到了n-1 号家门口,手里最多能有多少钱”
n数字太大了,那么开始划分子问题:让 k 从 0 到 n,看一下dp[k]
根据题意,dp[k]是max(情况①,情况②)。这边笔者借用一下题解的图片
由于题目设定要求,你不能把k间房子全给偷了。
配合图片,那么两种小偷选择: ①不偷第k间房子,那么dp[k] = dp[k-1] ; ②决定偷第k间房子,那么为了保证第k间房子不会报警,第 k-1房子就不会偷,那么dp[k] = dp[k-2] + money[k]
这里相当于是把第k间房子之前的房子,偷到的钱给【黑盒化】了(即不必深究dp[i-1]是怎么偷的,因为递归会给你答案)
那么dp[i] = max(dp[i-1],dp[i-2]+money[k])
然后就是数组的初始化:dp[0]表示偷0号,那dp[0] = money[0]即可
dp[1] 的话就要看 0号 和 1号谁更有钱了,那么dp[1]= max(money[0],money[1])
从i = 2 开始遍历,初始化完成。
C++代码
class Solution {
public:
int rob(vector<int>& nums) {
if(nums.size() == 1)
return *nums.begin();//类似指针:获取nums的begin迭代器后,通过 * 运算符来解引用
// 老实dp
// dp[i] :到了i号门口 手头的钱(偷i个屋子能偷到的钱)
// 状态:① 没偷钱:dp[i] = dp[i-1] ②偷了钱:dp[i-2] + nums[i](要偷钱的话,前面的房子说明没偷)
vector<int> dp(nums.size(),-1);//房子也是从0- n-1
dp[0] = nums[0];
dp[1] = max(nums[0],nums[1]);
int i = 0,len = nums.size();
for(int i=2;i<len;i++)
{
dp[i] = max(dp[i-1],dp[i-2] + nums[i]);
}
return dp[len-1];
}
};