一.題目連接:https://leetcode.com/problems/knight-probability-in-chessboard/算法
二.題目大意:數組
給定一個N*N的棋盤和一個初始座標值(r,c),開始時騎士在初始座標處,騎士會進行移動,而且騎士移動的時候這隻能按照以下的移動方式:優化
即一共有8個可能移動的方向,而且騎士向每一個方向移動的機率都是相同的(即$1/8$) 。求騎士移動K步後,還在棋盤以內的機率。spa
三.題解:code
直觀來看,這道題可有用DFS和DP,但不知道DFS會不會超超時,因此我就直接用了DP的思路去解決這個問題。具體以下:
blog
定義一個三維double型數組$dp$,其中$dp[k][i][j]$表示騎士從座標$(i,j)$處開始移動K步後還在棋盤內的機率。很顯然,第K步的結果實收第K-1步的結果影響的,而K-1步的結果可能有8種,因而,能夠寫出狀態轉移方程:leetcode
$$dp[k][i][j] = 1/8(dp[k-1][i - 2][j + 1] + dp[k-1][i - 2][j - 1] + dp[k-1][i - 1][j - 2] + dp[k-1][i - 1][j + 2] + dp[k-1][i + 2][j + 1] + dp[k-1][i + 2][j - 1] + dp[k-1][i + 1][j - 2] + dp[k-1][i + 1][j + 2])$$get
而且當K=0的時候,對於棋盤內全部的座標點的,相應的機率必爲1,因此初始狀態的方程爲:it
$$ dp[0][i][j] = 1$$io
好了,根據這兩個方程,就能夠直接寫代碼了:
class Solution { public: double knightProbability(int N, int K, int r, int c) { double dp[101][26][26];//dp數組,用於存儲狀態 int direction[][2] = {{-2,1},{-2,-1},{-1,-2},{-1,2},{2,1},{2,-1},{1,-2},{1,2}};//方向向量,表示8個可能的方向 if(K == 0)//特殊輸入的判斷 return 1.0; if(r < 0 || c < 0 || c >= N || r >= N)//特殊輸入的判斷 return 0.0; for(int i = 0; i < N; i++)//初始狀態 for(int j = 0; j < N; j++) dp[0][i][j] = 1.0; for(int num = 1; num <= K; num++)//注意循環的順序 for(int i = 0; i < N; i++) for(int j = 0; j < N; j++) { double tmp = 0; for(int l = 0; l < 8; l++)//分別計算8個可能的方向 { int x = i + direction[l][0]; int y = j + direction[l][1]; if(x < 0 || y <0 || x >= N || y >= N)//不符合條件,則進行下次循環 continue; tmp += (1.0 / 8.0)*dp[num - 1][x][y]; } dp[num][i][j] = tmp; } return dp[K][r][c];//返回最終的結果 } };
該算法的時間複雜度爲O(k*N^2),空間複雜度爲O(K*N^2),應該能夠繼續優化的,但不想繼續作了....
此外,有幾點須要注意的是:
1.最外層循環必須是K,這樣的話才能保證,在求父問題的時候,相應的子問題已經求出結果了。
2.要對每種可能的方向,判斷他們以前是否是還在棋盤內。