1.0 无障碍,右下走,代价相等
   
m*n的方格,要求最短路径从左上角到右下角,有多少种走法(有多少条最短路径)?
     
   
解法一:
    动态规划,
    
     
      因为是最短路径,所以每一步只能往右或者往下走
     
    
    ,那么到第n步(不在第一行或者第一列)只有两种方式:从上往下来的,或者左往右来的。
   
所以可设二维数组:dp[m][n],dp[i][j]即从入口(a[0][0])走到a[i][j]的最短路径数目,容易推知:
    
     状态方程:
     
      dp[i][j] = dp[i][j-1] + dp[i-1][j]
     
    
   
    
     边界条件:
     
      dp[0][j] 和 dp[i][0]
     
    
   
代码很容易写出来:
int[][] dp = new int[m][n];   // 自动赋值0
//第一行第一列,边界条件
for(int i = 0; i < n; i++)
    dp[0][i] = 1;
for(int i = 1; i < m; i++)    // dp[0][0]已经赋过值
    dp[i][0] = 1;
for(int i = 1; i < m; i++){
    for(int j = 1; j < n; j++)
        dp[i][j] = dp[i][j-1]+dp[i-1][j];
}
return dp[m-1][n-1]l;
    
解法二:
再看这个图:
     
   
其实要最快走到出口,必然要往右走n步,往下走m步,总共要走m+n步。同样地,如果一个走法向走了n步,向下走了m步,那么该走法的路径一定是一条最短路径。
即:往右走n步,往下走m步和最短路径是充分必要条件。
    再回到问题,对于路途中的当前步,想往下走(m步没有用完的前提下),还是往右走(n步没有用完的前提下)都是任意的。但是,
    
     
      如果确定了哪些步往右走,则往下走的步数也确定了
     
    
    ,如下图:
   
     
   
红色(往右走)确定了以后,蓝色自然就确定了。即在m+n步中选好了那些步往右(共n步),剩下的都是往下。
这就是数学中的组合问题(not排列),eg:给m+n个球上色(球标号1~m+n):只能上黑色或者白色,有多少种着色法。
    所以问题变成从m+n步中挑出n/m步:
    
    或者
    
    :
   
     
   
    代码完全是计算
    
    或者
    
    的问题:
   
    
    (高中数学知识)
   
    PS:
    
    是排列(有顺序),
    
    是组合(无顺序)
   
上代码:
int count = 1;
for(int i = m+n; i > 0; i--){
    if(i > n)
        count *= i;
    else
        count /= i;
}
return count;
    变式:有障碍,右下走,代价相等
   
m*n的方格,只能往右或者往下走,其中有一些格子不能走,用值-1表示,其余的格子可以走,用值1表示,现在要求最短路径从左上角到右下角,有多少种走法(有多少条最短路径)?
     
   
这个只是在边界初始化和迭代计算状态方程时加上判断条件即可,并且障碍物的
看代码:
int[][] dp = new int[m][n];   // 自动赋值0
//第一行第一列,边界条件
for(int i = 0; i < n; i++){
    if(1 == a[0][i])
        dp[0][i] = 1;
     else
        break;   // 后面都是默认的0值(0条路径)
}
for(int i = 0; i < m; i++){
    if(1 == a[i][0])
        dp[i][0] = 1;
     else
        break;   // 后面都是默认的0值(0条路径)
}     
for(int i = 1; i < m; i++){
    for(int j = 1; j < n; j++){
        if(1 == a[i][0])                       // 障碍物的dp不计算,还是默认的0,不影响
            dp[i][j] = dp[i][j-1]+dp[i-1][j];   
    } 
}
return dp[m-1][n-1]l;
    网上也有其他解析:
    2.0 无障碍,右下走,代价不等
   
    m*n的方格,要求
    
     只能往右走或者往下走
    
    ,从左上角到右下角的最短路径是多少?
   
    题目示例:
    
     leetcode 64. 最小路径和
    
   
如图:
     
   
解答:
思路:还是使用dp,只是状态方程和边界条件变了:
可设二维数组:dp[m][n],dp[i][j]即从入口(a[0][0])走到a[i][j]的最短路径数目,容易推知:
    
     状态方程:dp[i][j] = min(dp[i][j-1] + dp[i-1][j]) + a[i][j]
    
   
    
     边界条件:本题dp[0][j] 和 dp[i][0] 成了前n项和的问题(因为只有一种走法:将第一行/列前面的全部走完)
    
   
代码很容易写出来:
int[][] dp = new int[m][n];   // 自动赋值0
//第一行第一列,边界条件
dp[0][0] = a[0][0];           
for(int i = 1; i < n; i++)               // dp[0][0]已赋值
    dp[0][i] = dp[0][i-1]+a[0][i];       // 迭代求和
for(int i = 1; i < m; i++)    
    dp[i][0] = dp[i-1][0]+a[i][0];
for(int i = 1; i < m; i++){
    for(int j = 1; j < n; j++)
        dp[i][j] = Math.min(dp[i][j-1],dp[i-1][j])+a[i][j];  // Math.min有多个重载方法,有int的
}
return dp[m-1][n-1]l;也可见:
    
     给定一个矩阵m,从左上角开始每次只能向右走或者向下走,最后达到右下角的位置
    
   
由于右下走比较简单,可以引入负代价:
    变式:
    
     无障碍,右下走,代价不等并可能为负值
    
   
    题目示例:
    
     leetcode
    
    
     174. 地下城游戏
    
   
    策略为
    
     自顶而下的动态规划
    
    。
   
参考1.0由无障碍到有障碍的变式,本题也有:
    变式:有障碍,右下走,代价不等(大于0)
   
    m*n的方格,只能往右或者往下走,其中有一些格子不能走,用值-1表示,其余的格子可以走,且代价不等(均不等于-1且小于等于10000),求从左上角到右下角的最短路径
    
     
      是多少
     
    
    ?
   
参考1.0的引入障碍后的变式:
① 第一行第一列只要碰到障碍,包括障碍在内的后面格子dp均是0(0条路径)
② 剩余的格子,从dp[1][1]开始,按照从左到右,从上往下的顺序,计算dp[i][j]。只有当前不是障碍时,才使用
    
     dp[i][j] = dp[i][j-1] + dp[i-1][j]
    
   
    
    
    若是障碍,则不处理(默认值为0)
   
本题是计算最小路径的大小,不是计算有多少条,则对应到上题,本题策略应该为:
    ① 第一行第一列采用累加策略(只有这一条路可走)计算dp,只要碰到障碍,包括障碍在内的后面格子dp均是
    
     
      正无穷
     
    
   
② 剩余的格子,从dp[1][1]开始,按照从左到右,从上往下的顺序,计算dp[i][j]。只有当前不是障碍时,才使用
    
     dp[i][j] = min(dp[i][j-1] + dp[i-1][j])+a[i][j]
    
   
    
    
    若是障碍,则赋值
    
     
      正无穷
     
    
   
代码:
int[][] dp = new int[m][n];   // 自动赋值0
//第一行第一列,边界条件
dp[0][0] = a[0][0];           
for(int i = 1; i < n; i++){    
    if(-1 != a[0][i])
        dp[0][i] = dp[0][i-1]+a[0][i];      // 迭代求和
    else{
        for(int j = i; j < n; j++)
            dp[0][i] = Integer.MAX_VALUE;   // Integer.MAX_VALUE来表示正无穷
    }
}
for(int i = 1; i < m; i++){   
    if(-1 != a[i][0])
        dp[i][0] = dp[i-1][0]+a[i][0];
    else{
        for(int j = i; j < m; j++) 
            dp[i][0] = Integer.MAX_VALUE;   
    }
}
for(int i = 1; i < m; i++){
    for(int j = 1; j < n; j++){
        if(-1 != a[i][0])
            dp[i][j] = Math.min(dp[i][j-1],dp[i-1][j])+a[i][j];
        else
            dp[i][j] = Integer.MAX_VALUE;   // Integer.MAX_VALUE来表示正无穷
    }
        
}
return dp[m-1][n-1]l;
    3.0 有障碍,任意走,代价相等(大于等于0)
   
    
     即一般性迷宫问题
    
   
m*n的方格,要求从左上角到右下角,可以上下左右走,但不能斜着走,1代表可以走,-1代表障碍物,求最短路径(保证有唯一解)?e.g.
     
   
其实就是最基础的图的最短路径问题,而本题是代价相同的图最短路径:
解法很经典:DFS或BFS
DFS做的:
BFS做的:
    练习的话可以看Leetcode
    
    
     1091. 二进制矩阵中的最短路径
    
   
还有:
    4.0 有障碍,任意走,代价不等(大于等于0)
   
    
     即一般性的图最短路径问题
    
   
图最短路径问题最“暴力”的解法就是BFS和DFS,只是相对3.0,本题的每步代价不一样,代码改动倒很小:
dp[i][j]更新时的条件由 XXX 改为 XXX。
它的变式很多,一个更为简单的变式就是:
    变式:无障碍,任意走,代价不等
   
即没有障碍的迷宫,其实以图的视角看,就是联通度更高的图而已,方法都是和上面的一样,甚至还简单些,不用判断是不是障碍。
当然,图的最短路径是个大学问,Dijkstra算法,plim算法,Flord算法
如果引入负权边,还有其他算法。(2,0虽然有负权边,但是只能右下走,dp一直取了最小值,没有使用DFS和BFS,故没有问题)
大学问,大学问啊。
 
