dp 问题总结
- 状态表示
- 状态转移方程
- dp 表的初始化
- 遍历dp表的顺序
- 考虑最后的返回值
class Solution {
public:
int uniquePaths(int m, int n) {
vector<vector<int>>dp(m + 1 , vector<int>(n + 1 , 0));
dp[0][1] = 1;
for(int i = 1; i <= m; i ++){
for(int j = 1; j <= n; j ++){
dp[i][j] = dp[i - 1][j] + dp[i][j - 1];
}
}
return dp[m][n];
}
};
class Solution {
public:
int minCostClimbingStairs(vector<int>& cost) {
int n = cost.size();
vector<int>dp(n + 1 , 0);
dp[0] = dp[1] = 0;
for(int i = 2; i <= n; i++){
dp[i] = min(cost[i - 1] + dp[i - 1] , cost[i - 2] + dp[i - 2]);
}
return dp[n];
}
};
class Solution {
public:
int tribonacci(int n) {
long long dp[40] = {0};
dp[0] = 0;
dp[1] = 1;
dp[2] = 1;
for(int i = 3; i < 40; i ++){
dp[i] = dp[i - 1] + dp[i - 2] + dp[i - 3];
}
return dp[n];
}
};
恶魔们抓住了公主并将她关在了地下城 dungeon 的 右下角 。地下城是由 m x n 个房间组成的二维网格。我们英勇的骑士最初被安置在 左上角 的房间里,他必须穿过地下城并通过对抗恶魔来拯救公主。
骑士的初始健康点数为一个正整数。如果他的健康点数在某一时刻降至 0 或以下,他会立即死亡。
有些房间由恶魔守卫,因此骑士在进入这些房间时会失去健康点数(若房间里的值为负整数,则表示骑士将损失健康点数);其他房间要么是空的(房间里的值为 0),要么包含增加骑士健康点数的魔法球(若房间里的值为正整数,则表示骑士将增加健康点数)。
为了尽快解救公主,骑士决定每次只 向右 或 向下 移动一步。
返回确保骑士能够拯救到公主所需的最低初始健康点数。
注意:任何房间都可能对骑士的健康点数造成威胁,也可能增加骑士的健康点数,包括骑士进入的左上角房间以及公主被监禁的右下角房间。
class Solution {
public:
int calculateMinimumHP(vector<vector<int>>& d) {
// 核心状态表示 以 { i , j} 所在位置为起点所需要的最低的健康点数 进行考虑
int m = d.size() , n = d[0].size();
vector<vector<int>>dp(m + 1 , vector<int>(n + 1 , INT_MAX));
//初始化操作
//为什么要将这两个点的值设置为1 考虑到一个问题
// 骑士从公主那个点出去之后 任然需要最低健康点为1(只有为1才能保证骑士存活)
// 并且求出公主点所在的dp值也仅仅需要这连个点为1
dp[m][n -1] = dp[m-1][n] = 1;
for(int i = m - 1; i >= 0; i -- ){
for(int j = n - 1; j >= 0; j --){
// 这里不不能将 -d[i][j] 放在括号内会存在 overflow 的情况
dp[i][j] = min(dp[i][j + 1] , dp[i + 1][j])- d[i][j];
// 考虑 {i , j} 所在位置可能血包值过大导致dp[i][j] 为负数不符合逻辑要求
// 故与 1 进行取 max 保证{i , j} 位置到重点的最低健康点为1
dp[i][j] = max(1 , dp[i][j]);
}
}
return dp[0][0];
}
};
等价题型: 按摩师——附上链接
你是一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警。
给定一个代表每个房屋存放金额的非负整数数组,计算你 不触动警报装置的情况下 ,一夜之内能够偷窃到的最高金额。
示例 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
// 核心思路 dp[i] 表示以 i 为结尾目前所能偷到的 最高金额
// 进一步考虑 i 目前位置所对应的数字 偷还是不偷
// 采用 f[i] 和 g[i] 来考虑
// f[i] 表示nums[i]一定偷盗的前提下的目前所能偷盗的最高金额
// g[i] 表示nums[i] 一定不偷的前提下目前所能偷盗的最高金额
// 这时 f[i] = g[i -1] + nums[i];
// g[i] 此时又分为两种情况 nums[i - 1] 偷或者不偷
// 这时 g[i] = max(f[i - 1] , g[i - 1]);
class Solution {
public:
int rob(vector<int>& nums) {
int n = nums.size();
if(n == 0) return 0;
//dp 表
vector<int>f(n , 0);
vector<int>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]);
}
};