Idiot-maker

  :: 首页 :: 博问 :: 闪存 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理 ::

https://oj.leetcode.com/problems/unique-paths/

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?

Note: m and n will be at most 100.

解题思路:

这道题其实是一个高中数学的排列组合题目。将格子视为一个二维数组a[m][n],从a[0][0]到a[m - 1][n - 1]总共需要走m-1+n-1=m+n-2步。其中,必然有n-1步往右,m-1部往下,区别就是哪n-1往右,或哪m-1往下。于是便可以转化为组合的题目,用C可以解决,就是Cm+n-2 m-1,或者Cm+n-2 n-1。因为根据公式,Cmn = Cmm -n.数学公式有Cmn = m!/((m - n)!*n!)。便可以直接求解了。

需要注意的是,如果直接定义int的阶乘的函数,很容易就越界了。这里需要引用java.math.BigInteger类,类似与C++中的long long类型。用它的乘法和除法,还要注意他的valueOf()与intValue()方法。

public class Solution {
    public static java.math.BigInteger factorial(int n){
        java.math.BigInteger result = java.math.BigInteger.valueOf(1);
        if(n == 0){
            return java.math.BigInteger.valueOf(1);
        }
        while(n > 1){
            result = result.multiply(java.math.BigInteger.valueOf(n));
            n--;
        }
        return result;
    }
    public int uniquePaths(int m, int n) {
        return (factorial(m + n - 2).divide(factorial(m - 1).multiply(factorial(n - 1)))).intValue();
    }
}

还可以定义化简后的阶乘,表示从m一直乘到n。

public class Solution {
    public static java.math.BigInteger factorial(int m, int n){ //m * (m - 1) * ... * (n + 1)
        java.math.BigInteger result = java.math.BigInteger.valueOf(1);
        while(m > n){
            result = result.multiply(java.math.BigInteger.valueOf(m));
            m--;
        }
        return result;
    }
    
    public int uniquePaths(int m, int n) {
        return (factorial(m + n - 2, n - 1).divide(factorial(m - 1, 1))).intValue();
    }
}

简单点,用double来解题。注意最后用Math.round()方法来四舍五入,否则可能会误差1。注意,这里用float不可以。

public class Solution {
    public static double factorial(int m, int n){ //m * (m - 1) * ... * (n + 1)
        double result = 1;
        while(m > n){
            result = result * m;
            m--;
        }
        return result;
    }
    
    public int uniquePaths(int m, int n) {
        return (int)Math.round(factorial(m + n - 2, n - 1) / factorial(m - 1, 1));
    }
}

再来一个动态规划的解题思路。我们将子问题,即状态定义为dp[m][n]表示到点a[m][n]的不同路径数量。那么要到点a[m][n],因为每次只能往下或往右,所以只有两种可能,从a[i - 1][j]或者a[i][j - 1]。要注意首先将到达最左侧和最上面的所有值设置为1,因为肯定只有1种可能路径。代码如下,时间复杂度为O(m*n)。

public class Solution {
    public int uniquePaths(int m, int n) {
        int[][] dp = new int[m][n]; //dp[m][n]表示到点a[m][n]的不同路径数量
        for(int i = 0; i < n; i++){
            dp[0][i] = 1;
        }
        for(int i = 0; i < m; i++){
            dp[i][0] = 1;
        }
        for(int i = 1; i < m; i++){
            for(int j = 1; j < n; j++){
                dp[i][j] = dp[i - 1][j] + dp[i][j - 1];
            }
        }
        return dp[m - 1][n - 1];
    }
}

 上述dp问题实际上是一个一维的dp,可以用一个一维数组便可解决问题,节省空间。因为dp[i][j]等于本列上一行+本行前一列,那么其实只要用一行数据便可,永远存最新(旧)的数据。算第j列时,dp[j] = dp[j - 1] + dp[j];用已经更新了的dp[j-1],即本行前一列的值,去加上未更新的dp[j],即本列上一行的值,算到的新的dp[j]就是新的本行本列的值了。这个思路有些巧妙,需要想通。

public class Solution {
    public int uniquePaths(int m, int n) {
        int[] dp = new int[n]; //dp[m][n]表示到点a[m][n]的不同路径数量
        //所有元素初始化值为0
        
        dp[0] = 1;
        
        for(int i = 0; i < m; i++){
            for(int j = 1; j < n; j++){
                dp[j] = dp[j - 1] + dp[j];
            }
        }
        return dp[n - 1];
    }
}

 

posted on 2015-01-23 13:16  NickyYe  阅读(179)  评论(0编辑  收藏  举报