题目源自于leetcode。
题目:A robot is located at the top-left corner of a m x n grid (marked 'Start' in the diagram below).The robot can only move either down or right at any point in time. The robot is trying to reach the bottom-right corner of the grid (marked 'Finish' in the diagram below).
How many possible unique paths are there?
本题思路:
对于m行n列的矩形,从左上角走到右下角需要走m-1+n-1步,其中m-1步是向下的,n-1步是向右的,这m-1+n-1步中的向下和向右任意组合,很明显存在的路径个数是组合数C(m-1+n-1, m-1)。
从图的角度来看,可以采用由小到大的递推思想进行,先考虑最接近右下角的点到目的地的路径个数,然后依次递推,逐步扩散直到左上角。
代码一:递推方式
class Solution {
public:
int uniquePaths(int m, int n) {
if(m == 0 || n==0)
return 0;
int i,j;
int **value = new int* [m];
for(i=0;i<m;i++)
value[i] = new int[n];
value[m-1][n-1] = 1;
for(i=m-2;i>=0;i--)
value[i][n-1] = value[i+1][n-1];
for(i=n-2;i>=0;i--)
value[m-1][i] = value[m-1][i+1];
for(i=m-2;i>=0;i--)
for(j=n-2;j>=0;j--)
value[i][j] = value[i+1][j] + value[i][j+1];
int result = value[0][0];
for(i=0;i<m;i++)
delete[] value[i];
delete[] value;
return result;
}
};
数学方法求组合数C(m-1+n-1, m-1)。求组合数的时候注意,按平常的求法,我是先求分子上的积,然后求分子的积,然后相除。但是在程序运行的时候如果这样做,容易导致运算乘法的时候溢出。所以我乘法和除法交叉进行,使得结果始终不会溢出。此时又产生一个问题,虽然最终的组合数一定是个整数,但是交叉乘除的中间过程可能产生小数,所以我用浮点数来做。最终结果需要再转换为整数。
最终的结果可能是这样的两种情况,比如10.99,需要转换为11;又如10.00,需要转换为10。显然这两个的转换策略是不同的,需要区分开。
class Solution {
public:
int uniquePaths(int m, int n) {
if(m == 0 || n==0)
return 0;
double result = 1;
int i;
for(i=0;i<m-1;i++)
{
result *= m + n - 2 - i;
result /= m-1-i;
}
if(result - (int)result > 0.01)
return result + 1;
else
return result;
}
};
上面两种方式比较起来:递推方式的时间性能好一些,因为只是做加法, 但是需要有O(m*n)的空间消耗;数学方法的空间性能是O(1),由于需要乘法和除法,所以时间上要比递推方式差一点点。
问题扩展:对上面的问题的条件加强,要求格路径必须只在矩形的一个反对角线的单独一侧,求满足这种要求的路径数。
显然这种情况下的路径数要比之前的问题的路径数少一些,但绝对不是它的一半。
如果矩形是正方形,那么该问题属于catalan数的问题(在另一篇博文中);
如果矩形不是正方形,那么是组合数学的又一个问题,可以直接参考定理8.5.3: