标签:复杂度 需要 problems 除了 暴力 多少 方式 code span
上一节中我们讲了一个炒鸡简单的动态规划的例子,主要讲述了动态规划的思路,即我们是怎么从暴力求解,进入到动态规划的思路的。本节中我们来关注另一道炒鸡简单和典型的动态规划的题。
假设你正在爬楼梯。需要 n 阶你才能到达楼顶。
假设你正在爬楼梯。需要 n 阶你才能到达楼顶。 每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢? 注意:给定 n 是一个正整数。 来源:力扣(LeetCode) 链接:https://leetcode-cn.com/problems/climbing-stairs
我在拿到这道题的时候,第一反应应该是这不就是高中的排列组合么,确实,这就是一道排列组合题,但是深入一想,啧,这个排列组合貌似有丢丢复杂,没法在第一时间得到f(n)的公式。此时再转念一想,要不用暴力求解的办法,暴力求解不是不可以,可以用递归实现。
int ans = 0; void temp(int n){ if(n==0) { ans++; return; } if(n<0) return; temp(n-1); // 走一步 temp(n-2); //走两步 }
但是很显然,上述暴力求解是经过一步一步的试探,最终得到了总共有几种走法,这样一来势必会有很多重复计算的过程,而且,再n很大的时候,递归深度太大,不现实,此处可以记住一个小技巧,也是在上一个小节中提到的,能用递归解决的问题,很可能就能用动态规划解决。
动态规划的核心是保存已经计算过的结果,通过已经计算出来的结果去推断出下一个状态的结果,翻译成人话就是,动态规划的核心就是状态转移方程。和递归相比,由于其能够存储中间结果,因此,有着很好的时间复杂度。
那么,这道题用动态规划的思想,如何解决呢? 或者说,他的状态转移方程是什么呢?我们假设,如果我们知道了状态 i 的路径 step1,那么 i+1 的路径会是怎样的呢?首先我们思考,状态 i+1 可以从哪些状态转移过来,很简单状态 i 向上买一步就可以到达状态 i+1,也就是说,在这种情况下( i 向上迈一步到达状态 i+1 ),状态 i+1 的到达路径和状态 i 的路径完全一致,即所有的路径再迈一步而已。题设中还有一种方式,即,迈两步,那么也就是说,状态 i+1 也可以由状态 i-1 迈两步到达,和之前分析的,在这种情况下,状态 i+1的路径和状态 i-1 的路径完全一致。那么我们可以得到,状态 i+1 是由状态i 和状态 i-1 到达,即,f( i+1 ) = f( i ) + f( i-1 );我们又可知, f( 1 ) = 1(迈一步就完了), f ( 2 ) = 2(两个一步,和一个两步),那么可以有以下代码。
public int climbStairs(int n) { switch (n){ case 1:return 1; case 2:return 2; default:{ int[] dp = new int[n+1]; dp[1] = 1; dp[2] = 2; for(int i=3; i<=n;i++){ dp[i] = dp[i-1] + dp[i-2]; // 状态 i 可以由 i-1 迈一个台阶到达,也可以由i-2迈两个台阶到达。 } return dp[n]; } } }
事实上,这一类问题有一个基于DP的通解,即加入一个step变量,此step变量指的是,最多一次能走几个台阶。显然step>=1,再此题中,step是2。
那么其通解可以由一下代码给出:
public int climbStairs(int n) { switch (n){ case 1:return 1; case 2:return 2; default:{ int[] dp = new int[n+1]; dp[1] = 1; dp[2] = 2; int step = 2; for(int i=3; i<=n;i++){ for (int j=1;j<=step;j++) dp[i] += dp[i-j]; // 在i-j的状态上走j步,就到了i的状态。 } return dp[n]; } } }
当然了,此题除了动态规划外,还可以用数学的方式们直接计算出公式,这种算法具备最佳的复杂度,但是也更难理解。
标签:复杂度 需要 problems 除了 暴力 多少 方式 code span
原文地址:https://www.cnblogs.com/establish/p/11621097.html