Leetcode題解:unique-path
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?
Note: m and n will be at most 100.
Example 1:
Input: m = 3, n = 2 Output: 3 Explanation: From the top-left corner, there are a total of 3 ways to reach the bottom-right corner: 1. Right -> Right -> Down 2. Right -> Down -> Right 3. Down -> Right -> Right
Example 2:
Input: m = 7, n = 3 Output: 28
題目給了一個n行m列的網格,一個機器人要從左上角走到右下角,並且機器人每次只能向右走或者向下走,問有多少種走法。
解法1:用組合數求解
很明顯,我們知道機器人一共有m-1次向右走,n-1次向下走,每一種走法就是n - 1個Down動作和m - 1個Right動作的排列,也就是。
因此我們可以只要計算出就可以得到答案,但是又有一個問題就是我們應該如何高效的計算一個組合排列數。
如果我們直接按公式: 來計算,那麼當m和n比較大時,很容易在計算分子的時候就溢位了,並不是很通用。
既然一次性算出容易溢位,那我們可以先從更小的子問題開始。我們可以用動態規劃的思想來逐步求解出。動態規劃思想中最核心的地方就是如何劃分子問題,以及如何通過子問題得到更大的問題的解。
通過公式: 我們可以將分解為更小的兩個子問題,我們只要算出了的值,便可以計算出。具體實現如下:
// golang 0ms 100%
func C(m int, n int) int {
//special cases
if m == 0 || n == 0 || m == n {
return 1
}
solutions := make([][]int, m + 1)
for i := 0;i < m + 1;i++ {
size := n + 1
if m < n {
size = m + 1
}
solutions[i] = make([]int,size)
}
//initialize
for i := 0;i < m + 1;i++ {
solutions[i][0] = 1
}
solutions[1][1] = 1
//dynamic programming
for i := 2;i < m + 1;i++ {
for j := 1;j < len(solutions[m]);j++ {
solutions[i][j] = solutions[i - 1][j] + solutions[i - 1][j - 1]
if i == m && j == n {
break
}
}
}
return solutions[m][n]
}
func uniquePaths(m int, n int) int {
total := m + n -2
sel := m - 1
if n - 1 < sel {
sel = n - 1
}
return C(total, sel)
}
程式碼中函式C即是來計算組合數的公式,它通過一個m+1行n+1列的矩陣solutions來儲存計算結果,solutions[m][n]代表的值。演算法先計算出m=0和m=1時的所有組合數,之後不斷迭代直至計算出。
解法2:將各個位置的路徑數作為子問題
這種解法依然是用動態規劃策略,但更加直觀。假設到達位置(i,j)的路徑數位solutions[i][j],那麼有:
這是因為機器人只能向右或向下移動,到達位置(i,j)必須經過(i-1,j)或(i,j-1)。演算法實現如下:
// golang 0ms 100%
func uniquePaths(m int, n int) int {
//special cases
if m == 1 || n == 1 {
return 1
}
solutions := make([][]int, n)
for i := 0;i < n;i++ {
solutions[i] = make([]int, m)
}
//initialize
for i := 0;i < n;i++ {
solutions[i][0] = 1
}
for j := 0;j < m;j++ {
solutions[0][j] = 1
}
//dynamic programming
for i := 1;i < n;i++ {
for j := 1;j < m;j++ {
solutions[i][j] = solutions[i - 1][j] + solutions[i][j - 1]
}
}
return solutions[n - 1][m - 1]
}
第二個解法通過稍加改動,便可以來更進一步解決unique-path-ii這道題。unique-path-ii在上面這道題的基礎上,在一些位置設定了障礙物。對於有障礙物的位置(i,j),令solutions[i][j]=0即可。
// golang 4ms 100%
func uniquePathsWithObstacles(obstacleGrid [][]int) int {
n := len(obstacleGrid)
m := len(obstacleGrid[0])
solutions := make([][]int, n)
for i := 0;i < n;i++ {
solutions[i] = make([]int, m)
}
//initialize
for i := 0;i < n;i++ {
if obstacleGrid[i][0] == 1 {
for j := i;j < n;j++ {
solutions[j][0] = 0
}
break
}
solutions[i][0] = 1
}
for j := 0;j < m;j++ {
if obstacleGrid[0][j] == 1 {
for i := j;i < m;i++ {
solutions[0][i] = 0
}
break
}
solutions[0][j] = 1
}
//dynamic programming
for i := 1;i < n;i++ {
for j := 1;j < m;j++ {
if obstacleGrid[i][j] == 1 {
solutions[i][j] = 0
continue
}
solutions[i][j] = solutions[i - 1][j] + solutions[i][j - 1]
}
}
return solutions[n - 1][m - 1]
}