题目链接:LeetCode62.不同路径;
由于是求从一个点到另一个点的路径有多少条,显而易见,可以采用深度优先搜索的方式,遍历所有路径,如果能够到达目标坐标的路径并统计路径数目然后返回。Java代码如下:
class Solution {
public int uniquePaths(int m, int n) {
return dfs(m,n,1,1);
}
public int dfs(int m,int n,int x,int y){
if(x==m&&y==n)
return 1;
if(x>m||y>n)
return 0;
int ans=0;
ans+=dfs(m,n,x+1,y);
ans+=dfs(m,n,x,y+1);
return ans;
}
}
然而很遗憾,采用深度优先搜索的方式会RE,提交结果如下:
既然暴力搜索会超时,那么就考虑记忆化搜索:用一个数组记录已经获取到了的结果,在递归过程中如果需要某个已经计算过的值,就直接返回,否则就行递归,并将该值记录在数组的对应下表元素中。采用记忆化搜索的方法,思路简单,结构清晰,通过以空间换时间的方式,极大地减小了时间复杂度。Java代码如下:
class Solution {
public int uniquePaths(int m, int n) {
int[][]rec=new int[m+1][n+1];
return dfs(m,n,1,1,rec);
}
public int dfs(int m,int n,int x,int y,int[][]rec){
if(x==m&&y==n)
return 1;
if(x>m||y>n)
return 0;
if(rec[x][y]!=0)
return rec[x][y];
int ans=0;
ans+=dfs(m,n,x+1,y,rec);
ans+=dfs(m,n,x,y+1,rec);
rec[x][y]=ans;
return rec[x][y];
}
}
提交结果如下:
看样子记忆化搜索的方法还挺不错的,就是空间复杂度是O(mn).
这道题采用动态规划也能做,由于这道题的思路很简单,可以很轻松地得到递推公式:dp[x][y]=dp[x-1][y]+dp[x][y-1],当然要注意dp数组的初始化。Java代码如下:
class Solution {
public int uniquePaths(int m, int n) {
int[][]dp=new int[m+1][n+1];
for(int i=1;i<=m;++i)
dp[i][1]=1;
for(int i=1;i<=n;++i)
dp[1][i]=1;
for(int i=2;i<=m;++i)
for(int j=2;j<=n;++j){
dp[i][j]=dp[i-1][j]+dp[i][j-1];
}
return dp[m][n];
}
}
当然,由于dp[x][y]的状态只与dp[x-1][y]和dp[x][y-1]有关,所以可以用滚动数组对空间复杂度进行优化,感兴趣的朋友可以自己实现,在此不再赘述。运行结果如下:
学过离散数学的朋友应该知道,本题就是组合数学中多重集的全排列问题。要从起点到达终点,需要向x轴方向走m个坐标,向y轴走n个坐标,不同的路径就对应着多重集**S={(m-1)x,(n-1)y}**的全排列问题,所以可以直接计算出答案:
(m+n-2)!/(m-1)!(n-1)!
但是需要注意的是,在题目给定的范围内进行阶乘计算的话会数据溢出,所以需要在计算分子的同时除以分母,从而避免数据溢出。感兴趣的朋友可以自己实现。
这道题可以说是一道很经典的入门题目,既可以用搜索求解,又可以用动态规划求解,还可以用组合数学的思想直接求解,是一道入门的好题目。