题目原型:
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?
Above is a 3 x 7 grid. How many possible unique paths are there?
基本思路:
这种题目有三种解法,第一种是递归,第二种的动态规划,第三种是组合。我只想到了前两种。
(1)递归:
超时,代码略。比较简单。超时。
(2)动态规划
对于任意一点(x,y),要么是从(x,y)的左相邻点(x,y-1)过来,要么是从(x,y)的上相邻点(x-1,y)过来。如果用f(x,y)来表示robot走到点(x,y)时的走法,那么f(x,y)=f(x-1,y)+f(x,y-1)。其中,base条件是。当(x,y)位于最上面一行或者最左边一列时,由于robot要么向右走要么向下走,显然只能有1种走法,即f(x,y)=1 if(x==1 or y==0)。
int uniquePaths(int m, int n)
{
int[][] num = new int[m][n];
for(int i = 0;i<m;i++)
num[i][0] = 1;
for(int i = 0;i<n;i++)
num[0][i] = 1;
for(int i = 1;i<m;i++)
for(int j = 1;j<n;j++)
{
num[i][j] = num[i-1][j]+num[i][j-1];
}
return num[m-1][n-1];
}
其中网上网友给出了一种优化的动态规划,只需要O(min(m,n))大小的一个一维数组就可以了,这样能把动态规划的空间复杂度再优化一下。起算法如下:
// version 2 动态规划 bottom-up 空间复杂度由O(m*n)变化为O(min(m,n));
class Solution {
public:
int uniquePaths(int m, int n) {
int s=(m<n)?m:n;
int l=(m>n)?m:n;
int * arr= new int [s];
for(int j=0;j<s;j++)
arr[j]=1;
for(int i=1;i<l;i++){
for(int j=1;j<s;j++){
arr[j]+=arr[j-1];
}
}
return arr[s-1];
}
};
******************************************************以下内容转自别人空间************************************************************************************
(3)排列组合
排列组合,挺巧妙的,正好在Cracking the Code Interview书中看到了这种解法。robot从第(1,1)点走到了第(m,n)点。它只能向右或者向下,不管它怎么走,它必然向右走了m-1步,向下走了n-1步。一共走了m-1+n-1步。而不同的走法,本质是向右或者向下构成的m-1+n-1长度的序列不同。走法的总数目,本质上是m-1+n-1个总步数中选出m-1个代表向右走的走法的个数,这个问题的另一种表述是,走法的总数目,本质上是m-1+n-1个总步数中选出n-1个代表向下走的走法的个数。这其实正是组合的小性质。
C(a+b, a)=C(a+b, b)
这样题目就转换为了一个数学计算了,求C(m-1+n-1, m-1)。
// version 3 组合数学角度求从m-1+n-1个数种选出m-1个数的组合的数目,即C(m-1+n-1, m-1)的数目。
class Solution {
public:
unsigned long long get_factorial(unsigned long long k){ //计算阶乘
if(k==0)
return 1;
else
return k*get_factorial(k-1);
}
int uniquePaths(int m, int n) {
unsigned long long b=0; //防止溢出,用unsigned long long
unsigned long long c=0;
if(m<n){ //从m-1和n-1中选出一个较小的数,节省计算时间。因为C(m+n, n)和C(m+n, m)计算结果相等,所以可以这么简化。
b=m-1;
c=n-1;
}else{
b=n-1;
c=m-1;
}
unsigned long long res=1;
unsigned long long keep_b=b;
while(b>0){
res*=(b+c);
b--;
}
res=res/get_factorial(keep_b);
return (int)res;
}
};