有一类比较有趣的算法题。题目要求的是,按照一定的规则,在一个一维或二维的空间中,走到某个点,一共有多少种走法。我在这里介绍一下我遇到过的题:
1、青蛙跳台阶
一只青蛙一次只能跳一阶或两阶台阶,总共要跳n阶(n>=1),求总共有多少种跳法?
令f(n)为“跳n级台阶共有多少种跳法”。
显然,f(1)=1,f(2)=2。
那么当n>2时,我们可以对青蛙的第一步进行分类讨论,它第一步要么跳1阶,要么跳2阶。
当它第一步跳一级台阶的时候,跳n阶共有f(n-1)种跳法。
当它第一步跳两级台阶的时候,跳n阶共有f(n-2)种跳法。
两种情况加起来,得到f(n)=f(n-1)+f(n-2)。这就是我们要得到的动态规划结果,这也是个斐波那契数列。
一般来说,不建议直接在代码中写成递归实现。递归算法虽然代码很简单,但效率却非常低。
详见https://blog.csdn.net/qq_33951180/article/details/52484080。
2、蚂蚁从原点爬到坐标x,y有多少种爬法?
一只蚂蚁位于坐标原点(0,0),它每次只能在x正方向上爬一个单位,或者往y正方向爬一个单位。请问它爬到坐标(x,y)有多少种爬法(x,y都大于0)?
解法1:动态规划
建立一个x+1行y+1列的动态规划矩阵dp。dp[i][j]表示“从原点爬到坐标i,j有多少种爬法”,i>=0,j>=0。
首先将dp初始化,将它的第一行第一列都初始化为1。因为如果i,j中其中一个为0,那就只有一种爬法(直接往那个方向爬)。
对其余的dp中的元素逐行填表,dp[i][j]=dp[i-1,j]+dp[i,j-1]。跟第一题青蛙跳台阶类似,分成“第一步往x方向走”和“第二步往y方向走”两种情况。
我们以x=3和y=3为例:
初始化:
0 | 1 | 2 | 3 | |
0 | 1 | 1 | 1 | 1 |
1 | 1 | |||
2 | 1 | |||
3 | 1 |
填入其余元素
0 | 1 | 2 | 3 | |
0 | 1 | 1 | 1 | 1 |
1 | 1 | 2 | 3 | 4 |
2 | 1 | 3 | 6 | 10 |
3 | 1 | 4 | 10 | 20 |
于是得到结果为dp[3][3]=20。
当然,这个方法可以优化空间复杂度,这里就不多说了。
解法2:直接用组合
其实有一种更加直接的思路。首先,根据题目的限定条件,蚂蚁爬到(x,y)点一定是爬了(x+y)步的!而且这x+y步中,一定有x步是往x方向走,y步是往y方向走。那问题其实就变为“从x+y步中取出x步,让这x步是往x方向走,共有多少种取法?”(又或者是“从x+y步中取出y步,让这y步是往y方向走,共有多少种取法?”)
因此,令f(x,y)为“爬到坐标(x,y)有多少种爬法”,f(x,y)=C(x+y,x)=C(x+y,y)。
3、从原点出发,一步只能向右走、向上走或向左走,每一步都只能走一个单位长度。恰好走N步且不经过已走的点共有多少种走法?
解法:动态规划。这道题的难点在于,“不经过已走的点”,这个条件应该如何处理。
用一个N*3维的动态规划矩阵,矩阵中,dp[i][0],dp[i][1],dp[i][2]分别表示,“最后一步是往上,左,右走的情况下,走i步有多少种走法”。
首先初始化dp的第一行,dp[1,0]=dp[1][1]=dp[1][2]=1。
dp[i][0]=dp[i-1][0]+dp[i-1][1]+dp[i-1][2],这个好理解,因为如果最后一步是往上走的,那走出这一步一定不会导致经过已有的点。
dp[i][1]=dp[i-1][0]+dp[i-1][1]的意思是说,如果最后一步是往左走的,那上一步不能是往右走的(如果上一步是往右走,那就回到上一步结束后的那个点了),因此dp[i][1]=dp[i-1][0]+dp[i-1][1]。
dp[i][2]=dp[i-1][0]+dp[i-1][2]同理。
最后,我们需要的结果f(N)= dp[N][0]+dp[N][1]+ dp[N][2]。