【LeetCode】16.动态规划系列——基础

总目录:

LeetCode系列导航目录

 

0.理论基础

0.1.概念

算法基础——动态规划

Dynamic Programming,简称DP,如果某一问题有很多重叠子问题,使用动态规划是最有效的。

所以动态规划中每一个状态一定是由上一个状态推导出来的,这一点就区分于贪心,贪心没有状态推导,而是从局部直接选最优的。

0.2.下手套路

(1)确定dp数组(dp table)以及下标的含义;

(2)确定递推公式;

(3)dp数组如何初始化;

(4)确定遍历顺序;

(5)举例推导dp数组进行验证,debug。

 

1.斐波那契数

1.1.问题描述

斐波那契数 (通常用 F(n) 表示)形成的序列称为 斐波那契数列 。该数列由 0 和 1 开始,后面的每一项数字都是前面两项数字的和。也就是:
F(0) = 0,F(1) = 1
F(n) = F(n - 1) + F(n - 2),其中 n > 1
给定 n ,请计算 F(n) 。
链接:https://leetcode.cn/problems/fibonacci-number

1.2.要点

dp[n]含义:第n项斐波那契数

递推公式:已给出

遍历方式:递归或迭代都可

1.3.代码实例

 1 class Solution {
 2 public:
 3     int fib(int n) {
 4         if(n<=1){
 5             return n;
 6         }
 7         int a=0,b=1;
 8         for(int i=2;i<=n;i++){
 9             b+=a;
10             a=b-a;
11         }
12 
13         return b;
14     }
15 };
View Code

 

2.爬楼梯

2.1.问题描述

假设你正在爬楼梯。需要 n 阶你才能到达楼顶。

每次你可以爬 12 个台阶。你有多少种不同的方法可以爬到楼顶呢?

链接:https://leetcode.cn/problems/climbing-stairs/

2.2.要点

dp[n]:到第n阶有多少种上楼方式

递推公式:要想到某台阶,取决于前面有哪些台阶可以一步到达这里。dp[n]=dp[n-1]+dp[n-2]

2.3.代码实例

 1 class Solution {
 2 public:
 3     int climbStairs(int n) {
 4         if(n<=2){
 5             return n;
 6         }
 7 
 8         int a=1,b=2;
 9         for(int i=3;i<=n;i++){
10             b+=a;
11             a=b-a;
12         }
13 
14         return b;
15     }
16 };
View Code

 

3.使用最小花费爬楼梯

3.1.问题描述

给你一个整数数组 cost ,其中 cost[i] 是从楼梯第 i 个台阶向上爬需要支付的费用。一旦你支付此费用,即可选择向上爬一个或者两个台阶。
你可以选择从下标为 0 或下标为 1 的台阶开始爬楼梯。
请你计算并返回达到楼梯顶部的最低花费。
链接:https://leetcode.cn/problems/min-cost-climbing-stairs

3.2.要点

dp[n]:到达n处时的最小累计代价

递推公式:最小累计代价为前面能一步到达本处的那些台阶的(累计代价+当前台阶代价)序列的最小值。dp[i]=min(dp[i-1]+cost[i-1],dp[i-2]+cost[i-2]);

3.3.代码实例

 1 class Solution {
 2 public:
 3     int minCostClimbingStairs(vector<int>& cost) {
 4         int dataLen=cost.size();
 5         if(dataLen<2){
 6             return 0;
 7         }
 8 
 9         vector<int> dp(dataLen+1,0);
10         for(int i=2;i<=dataLen;i++){
11             dp[i]=min(dp[i-1]+cost[i-1],dp[i-2]+cost[i-2]);
12         }
13 
14         return dp.back();
15     }
16 };
View Code

 

4.不同路径

4.1.问题描述

一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为 “Start” )。
机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为 “Finish” )。
问总共有多少条不同的路径?

链接:https://leetcode.cn/problems/unique-paths

 

4.2.要点

dp[m][n]:到达(m,n)处时的路径数量

递推公式:到达(m,n)处只能来自于(m-1,n)和(m,n-1),所以dp[i][j]=dp[i-1][j]+dp[i][j-1];

4.3.代码实例

 1 class Solution {
 2 public:
 3     int uniquePaths(int m, int n) {
 4         if(m<=1||n<=1){
 5             return 1;
 6         }
 7         vector<vector<int>> dp(m,vector<int>(n,1));
 8         for(int i=1;i<m;i++){
 9             for(int j=1;j<n;j++){
10                 dp[i][j]=dp[i-1][j]+dp[i][j-1];
11             }
12         }
13 
14         return dp[m-1][n-1];
15     }
16 };
View Code

 

5.不同路径——有障碍

5.1.问题描述

一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为 “Start” )。
机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为 “Finish”)。
现在考虑网格中有障碍物。那么从左上角到右下角将会有多少条不同的路径?
网格中的障碍物和空位置分别用 1 和 0 来表示。

链接:https://leetcode.cn/problems/unique-paths-ii

5.2.要点

dp[m][n]:到达(m,n)处时的路径数量

递推公式:到达(m,n)处只能来自于(m-1,n)和(m,n-1),所以dp[i][j]=dp[i-1][j]+dp[i][j-1];

注意:最上边的一行有障碍会造成右侧的都不可达,最左一列有障碍会造成下方的都不可达,起点或终点有障碍时肯定不可达。

5.3.代码实例

 1 class Solution {
 2 public:
 3     int uniquePathsWithObstacles(vector<vector<int>>& obstacleGrid) {
 4         int row=obstacleGrid.size();
 5         if(row==0){
 6             return 0;
 7         }        
 8         int col=obstacleGrid[0].size();
 9         if(col==0){
10             return 0;
11         }
12 
13         if(obstacleGrid[0][0]==1||obstacleGrid[row-1][col-1]==1){
14             return 0;
15         }
16 
17         //动态规划
18         vector<vector<int>> dp(row,vector<int>(col,1));
19         for(int i=0;i<row;i++){
20             for(int j=0;j<col;j++){     
21                 if(obstacleGrid[i][j]==1){
22                     dp[i][j]=0;
23 
24                     //如果是两条边缘
25                     if(i==0){
26                         for(int k=j;k<col;k++){
27                             dp[i][k]=0;
28                         }
29                     }
30                     if(j==0){
31                         for(int k=i;k<row;k++){
32                             dp[k][j]=0;
33                         }
34                     }
35                     continue;
36                 }
37 
38                 if(i==0||j==0){
39                     continue;
40                 }
41                 dp[i][j]=dp[i-1][j]+dp[i][j-1];
42             }
43         }
44 
45         return dp[row-1][col-1];
46     }
47 };
View Code

 

6.整数拆分,剪绳子问题,使乘积最大

6.1.问题描述

给定一个正整数 n ,将其拆分为 k正整数 的和( k >= 2 ),并使这些整数的乘积最大化。

返回 你可以获得的最大乘积 。

链接:https://leetcode.cn/problems/integer-break/

6.2.要点

dp[n]:n长的绳子切割得到的最大乘积

递推公式:对于n可以裁为i和n-i,对于n裁或者不裁、对于n裁了之后得到的i和n-i是否继续裁,取决于比较谁是最大值。

dp[n]=max(

dp[n],

i*(n-i),

i*dp[n-i],

dp[i]*(n-i),

dp[i]*dp[n-i])

6.3.代码实例

 1 class Solution {
 2 public:
 3     int integerBreak(int n) {
 4         vector<int> dp(n + 1, 0);
 5         dp[1] = 1;
 6         for (int i = 2; i <= n; i++) {
 7             for (int j = 1; j <= i/2; j++) {
 8                 //这里面包含了拆或者不拆、拆多长的抉择
 9                 dp[i] = max(dp[i], (max(j, dp[j])) * (max(i - j, dp[i - j])));
10             }
11         }
12 
13         return dp[n];
14     }
15 };
View Code

 

7.不同的二叉搜索树

7.1.问题描述

给你一个整数 n ,求恰由 n 个节点组成且节点值从 1n 互不相同的 二叉搜索树 有多少种?返回满足题意的二叉搜索树的种数。

链接:https://leetcode.cn/problems/unique-binary-search-trees/

7.2.要点

dp[n]:n个节点有多少种二叉搜索树构型

递推公式:特殊题型,卡特兰公式问题。

对于n个节点,每个节点都有当根节点的情况f(i),dp[n]=f(1)+f(2)+...+f(n)

对于f(i)构成的搜索树中,其左子树有i-1个节点在构成dp[i-1]种搜索树,右边有n-i个节点构成的的dp[n-i]种搜索树,所以f(i)=dp[i-1]*dp[n-i]

综合以上可得dp[n]=dp[0]*dp[n-1]+dp[1]*dp[n-2]+...+dp[n-1]*dp[0],这就是卡特兰公式

7.3.代码实例

 1 class Solution {
 2 public:
 3     int numTrees(int n) {
 4         vector<int> dp(n+1,0);
 5         dp[0]=1;
 6         dp[1]=1;
 7         for(int i=2;i<=n;i++){
 8             //dp[n]=dp[0]*dp[n-1]+dp[1]*dp[n-2]+...+dp[n-1]*dp[0]
 9             for(int j=1;j<=i;j++){
10                 dp[i]+=dp[j-1]*dp[i-j];
11             }
12         }
13 
14         return dp[n];
15     }
16 };
View Code

 

 

 

xxx.问题

xxx.1.问题描述

111

xxx.2.要点

222

xxx.3.代码实例

333

posted @ 2022-12-27 18:00  啊原来是这样呀  阅读(46)  评论(0编辑  收藏  举报