class Solution {
public:
int fib(int n) {
if (n < 2) return n;
vector<int> f(n + 1);
f[0] = 0;
f[1] = 1;
for (int i = 2; i <= n; i ++)
f[i] = f[i - 1] + f[i - 2];
return f[n];
}
};
- 链接https://leetcode.cn/problems/climbing-stairs/
- 解题思路:动态规划
- 状态表示:f[i]表示爬到第 i 阶楼梯有多少种方法
- 状态计算:第i阶楼梯可以从第 i-1 阶爬上来或从第 i-2 阶爬上来,所以
f[i] = f[i-1] + f[i-2]
- 起始状态:楼梯从第1阶开始算起,由状态计算表达式可知f[3]由f[2]和f[1]推导得出,所以初始化
f[2] = 1, f[1] = 1
- leetcode解题代码
class Solution {
public:
int climbStairs(int n) {
if (n == 1) return 1;
vector<int> f(n + 1);
f[1] = 1;
f[2] = 2;
for (int i = 3; i <= n; i ++)
f[i] = f[i - 1] + f[i - 2];
return f[n];
}
};
- 链接https://leetcode.cn/problems/min-cost-climbing-stairs/
- 解题思路:动态规划
- 注意:站在当前台阶不需要花费,只有从当前台阶向上爬才需要花费
- 状态表示:f[i]表示爬到第 i 阶楼梯的最低花费是多少
- 状态计算:
第 i 阶楼梯可以从第 i-1 阶爬上来或从第 i-2 阶爬上来
从第 i-1 阶爬上来的花费是f[i-1] + cost[i-1]
从第 i-2 阶爬上来的花费是f[i-2] + cost[i-2]
所以f[i] = min(f[i-1] + cost[i-1],f[i-2] + cost[i-2])
- 起始状态:由题意可知可以选择从下标为 0 或下标为 1 的台阶开始爬楼梯楼梯从第 1 阶开始算起,由状态计算表达式可知f[2]由f[1]和f[0]推导得出,所以初始化
f[0] = 0, f[1] = 0
- leetcode解题代码
class Solution {
public:
int minCostClimbingStairs(vector<int>& cost) {
int n = cost.size();
vector<int> f(n + 1);
f[0] = 0;
f[1] = 0;
for (int i = 2; i <= n; i ++)
f[i] = min(f[i - 1] + cost[i - 1], f[i - 2] + cost[i - 2]);
return f[n];
}
};
- 链接https://leetcode.cn/problems/unique-paths/
- 解题思路:动态规划
- 状态表示:f[i][j]表示移动到下标为 (i, j) 的格子共有多少条不同的路径
- 状态计算:由于只能向下或者向右移动一步,所以 (i, j) 只能由 (i-1, j)和 (i, j-1) 得到
所以f[i][j] = f[i - 1][j] + f[i][j - 1]
注意状态表示是路径不是步数,所以状态计算不需要+1
- 起始状态:由状态计算可知,只能向下或向右更新状态,所以最上方的一行和最左边的一列均需要初始化,由于只有一条路径所以初始化为1,
f[i][0] = 1, f[0][j] = 1
- leetcode解题代码
class Solution {
public:
int uniquePaths(int m, int n) {
vector<vector<int>> f(m + 1, vector<int>(n + 1));
for (int i = 0; i < m; i ++) f[i][0] = 1;
for (int j = 0; j < n; j ++) f[0][j] = 1;
for (int i = 1; i < m; i ++)
for (int j = 1; j < n; j ++)
f[i][j] = f[i - 1][j] + f[i][j - 1];
return f[m - 1][n - 1];
}
};
class Solution {
public:
int uniquePathsWithObstacles(vector<vector<int>>& grid) {
int n = grid.size(), m = grid[0].size();
if (grid[0][0] == 1 || grid[n - 1][m - 1] == 1) return 0;
vector<vector<int>> f(n + 1, vector<int>(m + 1));
for (int i = 0; i < n && grid[i][0] == 0; i ++)
f[i][0] = 1;
for (int j = 0; j < m && grid[0][j] == 0; j ++)
f[0][j] = 1;
for (int i = 1; i < n; i ++)
for (int j = 1; j < m; j ++){
if (grid[i][j] == 0)
f[i][j] = f[i - 1][j] + f[i][j - 1];
}
return f[n - 1][m - 1];
}
};
- 链接https://leetcode.cn/problems/minimum-path-sum/
- 解题思路:动态规划
- 状态表示:f[i][j]表示移动到下标为 (i, j) 的格子路径上的数字总和为最小
- 状态计算:由于只能向下或者向右移动一步,所以 (i, j) 只能由 (i-1, j)和 (i, j-1) 得到
所以f[i][j] = min(f[i - 1][j] + f[i][j - 1]) + grid[i][j]
- 起始状态:由状态计算可知,只能向下或向右更新状态,所以最上方的一行和最左边的一列均需要初始化
- leetcode解题代码
class Solution {
public:
int minPathSum(vector<vector<int>>& grid) {
int n = grid.size(), m = grid[0].size();
vector<vector<int>> f(n + 1, vector<int>(m + 1));
if (n)
f[0][0] = grid[0][0];
for (int i = 1; i < n; i ++)
f[i][0] = f[i - 1][0] + grid[i][0];
for (int j = 1; j < m; j ++)
f[0][j] = f[0][j - 1] + grid[0][j];
for (int i = 1; i < n; i ++)
for (int j = 1; j < m; j ++)
f[i][j] = min(f[i - 1][j], f[i][j - 1]) + grid[i][j];
return f[n - 1][m - 1];
}
};
- 链接https://leetcode.cn/problems/integer-break/
- 解题思路:动态规划
- 状态表示:f[i]表示拆分正整数 i 可以获得的最大乘积
- 状态计算:f[i]可以拆分成 j 和 i-j,j的范围[1, i-1]
有两种情况,i-j 不再拆分,乘积为j * (i - j)
i-j 继续拆分,乘积为j * f[i-j]
(为什么不拆分j呢?因为j是从1开始遍历的,f[i-j]包括了f[j]的所有情况)
最大乘积两者取max即可
f[i] = max(f[i], max(j * (i - j), j * f[i - j]))
- 起始状态:由题目可知 0 和 1 不可被拆分,所以从2开始初始化,
f[2] = 1
- leetcode解题代码
class Solution {
public:
int integerBreak(int n) {
vector<int> f(n + 1);
f[2] = 1;
for (int i = 3; i <= n; i ++)
for (int j = 1; j < i; j ++)
f[i] = max(f[i], max(j * (i - j), j * f[i - j]));
return f[n];
}
};