题目描述
知识点
动态规划
结果
实现
码前思考
- 今天一天都在写动态规划,写的脑子有些迷迷糊糊的,去看了一篇文章——动态规划解题套路框架。虽然还是一些概念,但是温故而知新,我更加深入的了解了重叠子问题和最优子结构。
- 这道题目应该不能算严格意义上的动态规划,因为它没有体现最优的概念,动态规划问题一般是用来求解最优化问题的。但是,这道题目和斐波拉契数列很像,还是把它看作动态规划吧。参考之前看的那篇文章的思路,我尝试使用解动态规划问题的框架来思考:
- 划分子问题:求到点
(
i
,
j
)
(i,j)
(i,j)的独立路径,由于到点
(
i
,
j
)
(i,j)
(i,j)只能由
(
i
−
1
,
j
)
(i-1,j)
(i−1,j)和
(
i
,
j
−
1
)
(i,j-1)
(i,j−1)而来,那么到
(
i
,
j
)
(i,j)
(i,j)的独立路径就是到
(
i
−
1
,
j
)
(i-1,j)
(i−1,j)和
(
i
,
j
−
1
)
(i,j-1)
(i,j−1)的独立路径之和,可以分析到
(
i
−
1
,
j
)
(i-1,j)
(i−1,j)和
(
i
,
j
−
1
)
(i,j-1)
(i,j−1)的独立路径是不会重叠的,它们是独立的。
综上,最优子结构中的划分子问题解决,通常这一步很关键,一般的话,我都是先用暴力来试探怎么求解问题,看能不能通过暴力来发现子问题的存在,这道题里由子问题到原问题比较简单,只是加法,在难一点的动态规划问题中情况会复杂很多,通常会涉及到各种if
和for
! - 确定状态:上述原问题和子问题的区别在于坐标,因此状态是坐标;
- 确定
dp
数组的意义:通常它的含义就是问题,因此是到坐标 ( i , j ) (i,j) (i,j)的独立路径数; - 确定边界条件:边界条件通常是数组的边界出现在越界的地方,这里就是第一列和第一行的
dp
值要初始化为1。 - 综上4点,我们就能得到状态转移方程了。
代码实现
class Solution {
public:
int uniquePaths(int m, int n) {
//dp数组
vector<vector<int>> dp(n+1,vector<int>(m+1,0));
dp[1][1] = 1;
//进行初始化,base case,虽然最早写,但是分析时是最晚分析的
for(int j=2;j<=m;j++){
dp[1][j] = 1;
}
for(int i=2;i<=n;i++){
dp[i][1] = 1;
}
//开始进行递推求解,一行一行算
for(int i=2;i<=n;i++){
for(int j=2;j<=m;j++){
dp[i][j] = dp[i][j-1]+dp[i-1][j];
}
}
return dp[n][m];
}
};
码后反思
- 唉,做动态规划的题目要脑袋里面时刻清醒,要记住动态规划来自于暴力穷举,只是对暴力穷举进行了优化,我们可以先从暴力入手,从暴力里面抽取动态规划思想;
- 加油,继续努力,多实践,多反思,多总结。