原理
-
动态规划简单说就是 当前结果仅依赖于几个子问题的结果
-
比如汉诺塔问题,把n个盘从A移动到C,仅依赖于:
①把n-1个盘A -->B (a步)
②把最后一个盘从A -->C (1步)
③把n-1个盘B–>C (b步)
先得到 res=a+b+1 -
而其中的a,b 又可以类似res一样分别分解成子问题,(b同理)
a=a(n-1)+b(n-1)+1
a(n-1)=a(n-2)+b(n-2)+1
…
a(2)=a(1)+b(1)+1
此时a(1)=b(1)=1
方法一:递归
-
力扣算法题62,原问题如下
因为只能向下向右走,假设nums()为路径数,所以
nums(m×n)=nums(m-1×n)+nums(m×n-1),
是一个动态规划问题 -
用递归法代码结构为
class Solution
{
public int uniquePaths(int m, int n)
{
if(m==1||n==1)
{
return 1;
}
int right=uniquePaths(m-1,n);
int down=uniquePaths(m,n-1);
return right + down
}
}
- 但此方法在m×n较大时会有 运行超时异常,原因是:递归是用栈实现的,m×n较大时,栈深度增加出现栈溢出。所以需要用循环的方式去写。
方法二:循环
- 把上面的递归程序用循环实现,先用二维数组保存每次的结算结果
class Solution {
public int uniquePaths(int m, int n) {
int dp[][]=new int[m][n];
for(int i=0;i<m;i++)
{
for(int j=0;j<n;j++)
{
if(i==0||j==0)
{
dp[i][j]=1;
}
else
{
dp[i][j]=dp[i-1][j]+dp[i][j-1];
}
}
}
return dp[m-1][n-1];
}
}
这样就不会超时了。
2. 这时候能优化一下,因为每次只需要上一次右和下的结果,尝试用新解覆盖旧解,就不需要m×n的数组了
3. 先观察上面代码的两层循环,i 循环一次,二维数组更新一行,直至第m行得出结果,更新的过程需要此行上一列的值 和 上一行的值
4. 可见,不需要上上行的信息只需上行和本行的值,上行计算完后,让 上行 的值覆盖 上上行(1×n数组A),再算下一行(用1×n数组B记录)。再用B覆盖A再算下一行…迭代…所以可把m×n数组改成 2×n 的数组
5. 再进一步优化,直接在A(1×n数组)上面算下一行的值,内层循环执行完后,A就已经变成了B,省去覆盖的过程,所以只需要1×n的数组
6. 代码如下
class Solution {
public int uniquePaths(int m, int n) {
int dp[]=new int[n];
for(int i=0;i<m;i++)
{
for(int j=0;j<n;j++)
{
if(i==0||j==0)
{
dp[j]=1;
}
else
{
dp[j]=dp[j]+dp[j-1];
}
}
}
return dp[n-1];
}
}
刚开始写博客的结构有些乱,表达也很生硬。。
菜鸡也想变强,晚安return 0;