Open azl397985856 opened 3 years ago
动态规划
class Solution {
public double knightProbability(int n, int k, int row, int column) {
int[][] directions = new int[][] {{2, 1}, {-2, -1}, {2, -1}, {-2, 1}, {1, 2}, {-1, -2}, {1, -2}, {-1, 2}};
double[][] dp = new double[n][n];
dp[row][column] = 1;
while (k-- > 0) {
double[][] dpNext = new double[n][n];
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
for (int[] dir : directions) {
int newI = i + dir[0];
int newJ = j + dir[1];
if (newI < 0 || newI >= n || newJ < 0 || newJ >= n) continue;
dpNext[newI][newJ] += dp[i][j] / 8;
}
}
}
dp = dpNext;
}
double res = 0;
for (double[] rows : dp) {
for (double cell : rows) {
res += cell;
}
}
return res;
}
}
学习一下别人的解法
public double knightProbability(int n, int k, int row, int column) {
double total = Math.pow(8, k);
Double[][][] cache = new Double[k + 1][n][n];
double live = f(row, column, k, n, cache);
return live / total;
}
public double f(int x, int y, int k, int n, Double[][][] cache) {
if (x < 0 || y < 0 || x > n - 1 || y > n - 1) {
return 0;
}
if (k == 0) {
return 1;
}
if (cache[k][x][y] != null) {
return cache[k][x][y];
}
double rs = 0;
rs += f(x - 1, y - 2, k - 1, n, cache);
rs += f(x - 1, y + 2, k - 1, n, cache);
rs += f(x + 1, y - 2, k - 1, n, cache);
rs += f(x + 1, y + 2, k - 1, n, cache);
rs += f(x + 2, y - 1, k - 1, n, cache);
rs += f(x - 2, y - 1, k - 1, n, cache);
rs += f(x + 2, y + 1, k - 1, n, cache);
rs += f(x - 2, y + 1, k - 1, n, cache);
cache[k][x][y] = rs;
return rs;
}
dfs+记忆化,防止爆搜超时
class Solution:
def knightProbability(self, n: int, k: int, row: int, column: int) -> float:
memos=defaultdict(int)
d=[(2,1),(-2,1),(2,-1),(-2,-1),(1,2),(1,-2),(-1,2),(-1,-2)]
def dfs(cnt,r,c):
if (cnt,r,c) in memos:
return memos[(cnt,r,c)]
if cnt==0:
if r==row and c==column:
return 1
else:
return 0
if r<0 or r>=n or c<0 or c>=n:
return 0
ans=0
for dd in d:
ans+=dfs(cnt-1,r+dd[0],c+dd[1])
ans/=8
memos[(cnt,r,c)]=ans
return ans
res=0
for i in range(n):
for j in range(n):
res+=dfs(k,i,j)
return res
时间 O(kn^2)
空间 O(kn^2)
思路
计算每轮在轮盘上出现的位置
代码
class Solution {
private int[][] dir = {{-1, -2}, {1, -2}, {2, -1}, {2, 1}, {1, 2}, {-1, 2}, {-2, 1}, {-2, -1}};
public double knightProbability(int N, int K, int r, int c) {
double[][] dp = new double[N][N];
dp[r][c] = 1;
for (int step = 1; step <= K; step++) {
double[][] dpTemp = new double[N][N];
for (int i = 0; i < N; i++){
for (int j = 0; j < N; j++) {
for (int[] direction : dir) {
int lastR = i - direction[0];
int lastC = j - direction[1];
if (lastR >= 0 && lastR < N && lastC >= 0 && lastC < N)
dpTemp[i][j] += dp[lastR][lastC] * 0.125;
}
}
}
dp = dpTemp;
}
double res = 0;
for (int i = 0; i < N; i++)
for (int j = 0; j < N; j++)
res += dp[i][j];
return res;
}
}
复杂度
时间复杂度:O(KN^2)
空间复杂度:O(N^2)
Go Code:
func knightProbability(n int, k int, row int, column int) float64 {
dp := make([][]float64, n)
for i := range dp {
dp[i] = make([]float64, n)
}
dp[row][column] = 1
for s := 0; s < k; s++ {
dpTemp := make([][]float64, n)
for i := range dpTemp {
dpTemp[i] = make([]float64, n)
}
for i:=0;i<n;i++ {
for j:=0;j<n;j++ {
for _,dir := range [][]int{{-1, -2}, {1, -2}, {2, -1}, {2, 1}, {1, 2}, {-1, 2}, {-2, 1}, {-2, -1}} {
x, y := i-dir[0], j-dir[1]
if x >= 0 && x < n && y >= 0 && y < n {
dpTemp[x][y] += dp[i][j] * 0.125
}
}
}
}
dp = dpTemp
}
var res float64
for i:=0;i<n;i++ {
for j:=0;j<n;j++ {
res += dp[i][j]
}
}
return res
}
复杂度分析
令 n 为数组长度。
DP with memo.
def knightProbability(self, N: int, K: int, r: int, c: int) -> float:
is_in_board = lambda r, c: 0 <= r < N and 0 <= c < N
directions = {(dx, dy) for dx in (-2, -1, 1, 2) for dy in (-2, -1, 1, 2) if abs(dx) + abs(dy) == 3}
memo = {}
def dfs(K, r, c):
nonlocal memo
if K == 0:
return 1
if (K, r, c) in memo:
return memo[(K, r, c)]
p = 0
for dx, dy in directions:
x, y = r + dx, c + dy
if is_in_board(x, y):
p += dfs(K - 1, x, y) / 8
memo[(K, r, c)] = p
return p
return dfs(K, r, c)
Time complexity: O(Kn^2).
Space complexity: O(Kn^2).
已知一个 NxN 的国际象棋棋盘,棋盘的行号和列号都是从 0 开始。即最左上角的格子记为 (0, 0),最右下角的记为 (N-1, N-1)。
现有一个 “马”(也译作 “骑士”)位于 (r, c) ,并打算进行 K 次移动。
如下图所示,国际象棋的 “马” 每一步先沿水平或垂直方向移动 2 个格子,然后向与之相垂直的方向再移动 1 个格子,共有 8 个可选的位置。
动态规划
//马每一次都有八种走法
//令 f[r][c][steps] 代表马在位置 (r, c) 移动了 steps 次以后还留在棋盘上的概率,根据马的移动方式,我们有以下递归:
//f[r][c][steps] = ∑f[r + dr][c + dc][steps - 1] * 1/8,求和是把8组(dr,dc)加在一起,因为是都移动到了r,c位置
//而上一轮最多有8个点可以通过移动一次到达(r,c),这一轮移动只有1/8的概率到达这个(r,c)点
//上式还可以等于∑... + ∑... + ∑...
//最后不断的发散出去,我们最后求结果,也不清楚马到底可以跳到哪些个位置,但只要遍历整个棋盘,累加起来即可
//但这样可能要一个三维数组,我们观察到状态转移方程只需要保存当前轮次(step - 1)和下一轮(step),所以用两个二维数组就可以
//用curDp代表当前轮次,用nextDp代表下一轮次
class Solution {
public double knightProbability(int n, int k, int row, int column) {
//马每一次都有八种走法
//令 f[r][c][steps] 代表马在位置 (r, c) 移动了 steps 次以后还留在棋盘上的概率,根据马的移动方式,我们有以下递归:
//f[r][c][steps] = ∑f[r + dr][c + dc][steps - 1] * 1/8,求和是把8组(dr,dc)加在一起,因为是都移动到了r,c位置
//而上一轮最多有8个点可以通过移动一次到达(r,c),这一轮移动只有1/8的概率到达这个(r,c)点
//上式还可以等于∑... + ∑... + ∑...
//最后不断的发散出去,我们最后求结果,也不清楚马到底可以跳到哪些个位置,但只要遍历整个棋盘,累加起来即可
//但这样可能要一个三维数组,我们观察到状态转移方程只需要保存当前轮次(step - 1)和下一轮(step),所以用两个二维数组就可以
//用curDp代表当前轮次,用nextDp代表下一轮次
//8种不同跳法
int[] dr = new int[]{1,2,2,1,-1,-2,-2,-1};
int[] cr = new int[]{2,1,-1,-2,-2,-1,1,2};
//特殊处理
if(k == 0){
return 1;
}
double[][] curDp = new double[n][n];
//最初,step = 0时,就是还没有跳的时候,马处在(row,column)的概率是1
curDp[row][column] = 1;
for(int step = 1; step <= k; step++){
double[][] nextDp = new double[n][n];
//开始遍历各个棋盘上的点,用这个棋盘点上的当前轮次概率 * 1/8 后累加到对应的棋盘上的跳跃后的点上
for(int i = 0; i < n; i++){
for(int j = 0; j < n; j++){
//8种不同跳法
for(int type = 0; type < 8; type ++){
int newRow = i + dr[type];
int newColumn = j + cr[type];
if(newRow >= 0 && newRow < n && newColumn >=0 && newColumn < n){
nextDp[newRow][newColumn] += curDp[i][j] / 8;
}
}
}
}
//在开始下一轮step前,更新curDp和nextDp
curDp = nextDp;
}
//最后要返回的结果
double p = 0;
//遍历curDp,把各个点的概率累加起来
for(int i = 0; i < n; i ++){
for(int j = 0; j < n; j ++){
p += curDp[i][j];
}
}
return p;
}
}
时间复杂度:$O(n^2k)$
空间复杂度:$O(n^2)$
var knightProbability = function(n, k, row, column) {
// 每一个点可以移动的位置
const MOVE = [
[2, 1],
[2, -1],
[-2, 1],
[-2, -1],
[1, 2],
[1, -2],
[-1, 2],
[-1, -2],
];
// dp[i][j] 表示在位置 [i,j] 的概率
let dp = Array.from({ length: n }).map(() => new Array(n).fill(0));
dp[row][column] = 1; // 百分百在
for (let s = 0; s < k; s++) {
// 每一步都依赖上一步的图,但是对应的概率要按新的来处理
const tempDp = Array.from({ length: n }).map(() => new Array(n).fill(0));
// 要走 k 步
for (let i = 0; i < n; i++) {
for (let j = 0; j < n; j++) {
for (let item of MOVE) {
// 每一个点都可以向 8 个位置走一遍
const tempR = i + item[0];
const tempC = j + item[1];
if (tempR >= 0 && tempR < n && tempC >= 0 && tempC < n) {
// 表明这个方向是可选的
tempDp[i][j] += dp[tempR][tempC] * 0.125;
}
}
}
}
dp = tempDp;
}
let res = 0;
for (let i = 0; i < n; i++) {
for (let j = 0; j < n; j++) {
res += dp[i][j];
}
}
return res;
};
class Solution {
public:
double knightProbability(int n, int k, int row, int column) {
vector<vector
时间复杂度:O(KN2) 空间复杂度:O(N^2)
class Solution:
def knightProbability(self, n: int, k: int, row: int, column: int) -> float:
memo={}
def dfs(row,col,k):
if row>=n or row<0 or col >= n or col<0:
return 1
if k==0:
return 0
directions=[(2,1),(2,-1),(1,-2),(-1,-2),(-2,-1),(-2,1),(-1,2),(1,2)]
out_bound_p=0
for direction in directions:
next_row=direction[0]+row
next_col=direction[1]+col
if (next_row,next_col,k-1) in memo:
out_bound_p+=1/8*memo[(next_row,next_col,k-1)]
else:
out_bound_p+=1/8*dfs(next_row,next_col,k-1)
memo[(row,col,k)]=out_bound_p
return out_bound_p
return 1-dfs(row,column,k)
Time: O(n^2*k)
Space: O(n^2*k)
class Solution:
def knightProbability(self, n: int, k: int, row: int, column: int) -> float:
dp = [[0] * n for _ in range(n)]
dp[row][column] = 1
res = 0.0
directions = [(1, 2), (1, -2), (2, 1), (2, -1), (-2, 1), (-2, -1), (-1, 2), (-1, -2)]
for i in range(k):
new_dp = [[0] * n for _ in range(n)]
for r in range(n):
for c in range(n):
for d in directions:
new_row, new_col = r + d[0], c + d[1]
if new_row < 0 or new_row >= n or new_col < 0 or new_col >= n:
continue
new_dp[r][c] += dp[new_row][new_col]
dp = new_dp
for i in range(n):
for j in range(n):
res += dp[i][j]
return res / float(8 ** k)
class Solution:
def knightProbability(self, n: int, k: int, row: int, column: int) -> float:
def isValid(x, y):
return x >= 0 and y >= 0 and x < n and y < n
direction = [(-1, -2), (-2, -1), (-2, 1), (-1, 2), (1, 2), (2, 1), (2, -1), (1, -2)]
dp = [[0 for _ in range(n)] for _ in range(n)]
dp[row][column] = 1
for _ in range(k):
dp2 = [[0 for _ in range(n)] for _ in range(n)]
for i in range(n):
for j in range(n):
for dx, dy in direction:
if isValid(i+dx, j+dy):
dp2[i+dx][j+dy] += dp[i][j] / 8
dp = dp2
return sum([dp[i][j] for i in range(n) for j in range(n)])
class Solution(object):
def knightProbability(self, N, K, r, c):
dp = [[0] * N for _ in range(N)]
dp[r][c] = 1
for _ in range(K):
dp2 = [[0] * N for _ in range(N)]
for r, row in enumerate(dp):
for c, val in enumerate(row):
for dr, dc in ((2,1),(2,-1),(-2,1),(-2,-1),
(1,2),(1,-2),(-1,2),(-1,-2)):
if 0 <= r + dr < N and 0 <= c + dc < N:
dp2[r+dr][c+dc] += val / 8.0
dp = dp2
return sum(map(sum, dp))
DP:
Python写法:
class Solution:
def knightProbability(self, n: int, k: int, r: int, c: int) -> float:
dp = [[0 for _ in range(n)] for _ in range(n)]
dp[r][c] = 1
direction = ((1,2),(2,1),(1,-2),(-2,1),(-1,-2),(-2,-1),(-1,2),(2,-1))
for _ in range(k):
dp2 = [[0 for _ in range(n)] for _ in range(n)]
for i in range(n):
for j in range(n):
val = dp[i][j]
for di, dj in direction:
ii = i + di
jj = j + dj
if 0 <= ii < n and 0 <= jj < n:
dp2[ii][jj] += val/8.0
dp = dp2[:]
return sum([sum(dp[i]) for i in range(n)])
Java:
class Solution {
public double knightProbability(int n, int k, int row, int column) {
int[][] directions = {{2, -1}, {2, 1}, {-2, -1}, {-2, 1},{-1, 2}, {1, 2}, {-1, -2}, {1, -2}};
double[][] dp = new double[n][n];
for (int K = 0; K < k + 1; K += 1){
double[][] dp2 = dp;
dp = new double[n][n];
for (int i = 0; i < n; i++){
for (int j = 0; j < n; j++){
if (K == 0){
dp[i][j] = 1;
continue;
}
double temp = 0;
for (int[] dirr: directions){
int ii = i + dirr[0];
int jj = j + dirr[1];
if (check(n,ii,jj)){
temp += dp2[ii][jj]/8.0;
}
}
dp[i][j] = temp;
}
}
}
return dp[row][column];
}
private boolean check(int size, int x, int y) {
return x >= 0 && x < size && y >= 0 && y < size;
}
}
TC: O(k*n^2) SC: O(n^2)
动态规划
class Solution {
public double knightProbability(int n, int k, int row, int column) {
double[][] dp = new double[n][n];
int[] dx = new int[]{2,2,1,1,-2,-2,-1,-1};
int[] dy = new int[]{1,-1,2,-2,1,-1,2,-2};
dp[row][column] = 1;
for( ; k > 0 ; k--){
double[][] dp2 = new double[n][n];
for(int i = 0 ; i < n ;i++){
for(int j = 0; j < n ;j++){
for(int d = 0; d < 8; d++){
int x = i + dx[d];
int y = j + dy[d];
if(x >= 0 && x < n && y >= 0 && y < n){
dp2[x][y] += dp[i][j] / 8.0 ;
}
}
}
}
dp = dp2;
}
double ans = 0.0;
for(double[] x : dp){
for(double y : x){
ans += y;
}
}
return ans;
}
}
时间复杂度:O(8knn) 空间复杂度:O(nn)
class Solution {
public double knightProbability(int n, int k, int row, int column) {
double[][] dp = new double[n][n];
int[] dr = new int[]{2, 2, 1, 1, -1, -1, -2, -2};
int[] dc = new int[]{1, -1, 2, -2, 2, -2, 1, -1};
dp[row][column] = 1;
for(; k > 0; k--) {
double[][] dp2 = new double[n][n];
for (int r = 0; r < n; r++){
for (int c = 0; c < n; c++){
for (int j = 0; j < 8; j++){
int cr = r + dr[j];
int cc = c + dc[j];
if (0 <= cr && cr < n && 0 <= cc && cc < n){
dp2[cr][cc] += dp[r][c] / 8.0;
}
}
}
}
dp = dp2;
}
double ans = 0.0;
for (double[] rows : dp){
for (double x: rows) ans += x;
}
return ans;
}
}
class Solution {
public double knightProbability(int N, int K, int r, int c) {
int[] dx = {1, 1, 2, 2, -1, -1, -2, -2};
int[] dy = {2, -2, 1, -1, 2, -2, 1, -1};
double[][] cur = new double[N][N];
cur[r][c] = 1;
for(int k = 0; k < K; k++){
double[][] nxt = new double[N][N];
for(int x = 0; x < N; x++){
for(int y = 0; y < N; y++){
for(int i = 0; i < 8; i++){
int newX = x + dx[i], newY = y + dy[i];
if(newX >= 0 && newX < N && newY >= 0 && newY < N){
nxt[newX][newY] += cur[x][y] / 8.0;
}
}
}
}
cur = nxt;
}
double res = 0.0;
for(double[] row : cur){
for(double p : row){
res += p;
}
}
return res;
}
}
时间复杂度:O(nnk) 空间复杂度:O(nn)
class 🐎在棋盘上的概率_688 {
int[][] moves = new int[][]{{2,-1},{2,1},{1,2},{1,-2},{-1,2},{-1,-2},{-2,1},{-2,-1}};
public double knightProbability(int n, int k, int row, int column) {
double[][] dp0 = new double[n][n];
// 初始状态
dp0[row][column] = 1;
for(;k > 0; k --) { // 只使用一次 可以直接 -- 省下来一个局部变量的空间
double[][] dp1 = new double[n][n];
for(int r = 0 ; r < n ; r ++) {
for(int c = 0 ; c < n ; c ++) {
for(int i = 0; i < moves.length; i++) {
int nextr = r + moves[i][0];
int nextc = c + moves[i][1];
if(nextc < 0 || nextc >= n || nextr < 0 || nextr >= n) continue; // 越界
dp1[nextr][nextc] += dp0[r][c] / 8.0; // // 每轮 概率累加 使用上一轮的结果来
}
}
}
dp0 = dp1;
}
double res = 0;
for(double[] i : dp0) {
for(double j : i) {
res += j;
}
}
return res;
}
}
dp为当前马还留在棋盘上的概率,所以初始化状态dp[row][column]为1。dpTemp代表的是移动一次之后的概率,为八个方向过来的概率的1/8之和(因为dp上的值有1/8的可能性移动到当前dpTemp的点上),返回dp的所有概率之和。
class Solution {
public:
double knightProbability(int n, int k, int row, int column) {
vector<vector<double>> dp(n, vector<double>(n, 0));
vector<vector<double>> dir = {{2, 1}, {2, -1}, {1, 2}, {1, -2},
{-1, 2}, {-1, -2}, {-2, 1}, {-2, -1}};
dp[row][column] = 1;
for (int i = 1; i <= k; ++i) {
vector<vector<double>> dpTemp(n, vector<double>(n,0));
for (int r = 0; r < n; ++r) {
for (int c = 0; c < n; ++c) {
for (auto d: dir) {
int prevR = r - d[0];
int prevC = c - d[1];
if (prevR >= 0 && prevR < n && prevC >= 0 && prevC < n) {
dpTemp[r][c] += dp[prevR][prevC] * 0.125;
} // valid
}
}
}
dp = dpTemp;
}
double ans = 0.0;
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
ans += dp[i][j];
}
}
return ans;
}
};
https://leetcode-cn.com/problems/knight-probability-in-chessboard/
思路:计算在每个点的dp概率,每一个位置可能有1/8的概率被到达
class Solution:
def knightProbability(self, n: int, k: int, row: int, column: int) -> float:
dp = [[0] * n for _ in range(n)]
dp[row][column] = 1
direction = [(2,1),(-2,1),(2,-1),(-2,-1),(1,2),(1,-2),(-1,2),(-1,-2)]
for _ in range(k):# 第k次移动,每次移动要记录到每个地方的概率
dp2 = [[0] * n for _ in range(n)]
for i in range(n):
for j in range(n):
for dx,dy in direction:
if 0<=i+dx<n and 0<=j + dy<n :
dp2[i+dx][j+dy] += dp[i][j] / 8
dp = dp2
return sum(map(sum, dp))
复杂度分析:
时间复杂度:O(n^2* k)
空间复杂度:O( n*n)
这题不会。。。。机械打卡。
/**
* @param {number} N
* @param {number} K
* @param {number} r
* @param {number} c
* @return {number}
*/
var knightProbability = function(N, K, r, c) {
// 每一个点可以移动的位置
const MOVE = [
[2, 1],
[2, -1],
[-2, 1],
[-2, -1],
[1, 2],
[1, -2],
[-1, 2],
[-1, -2],
];
let res = 0;
// dp[i][j] = 落在 i j 的概率
let dp = Array.from({ length: N }).map(() => new Array(N).fill(0));
dp[r][c] = 1;
for (let s = 0; s < K; s++) {
const tempDp = Array.from({ length: N }).map(() => new Array(N).fill(0));
for (let i = 0; i < N; i++) {
for (let j = 0; j < N; j++) {
for (let item of MOVE) {
const tempR = i + item[0];
const tempC = j + item[1];
if (tempR >= 0 && tempR < N && tempC >= 0 && tempC < N) {
// 表明该方向还是在棋盘中
tempDp[i][j] += dp[tempR][tempC] * 0.125;
}
}
}
}
dp = tempDp;
}
for (let i = 0; i < N; i++) {
for (let j = 0; j < N; j++) {
res += dp[i][j];
}
}
return res;
};
title: "Day 59 688. “马”在棋盘上的概率" date: 2021-11-07T20:37:09+08:00 tags: ["Leetcode", "c++", "DP"] categories: ["91-day-algorithm"] draft: true
已知一个 NxN 的国际象棋棋盘,棋盘的行号和列号都是从 0 开始。即最左上角的格子记为 (0, 0),最右下角的记为 (N-1, N-1)。
现有一个 “马”(也译作 “骑士”)位于 (r, c) ,并打算进行 K 次移动。
如下图所示,国际象棋的 “马” 每一步先沿水平或垂直方向移动 2 个格子,然后向与之相垂直的方向再移动 1 个格子,共有 8 个可选的位置。
现在 “马” 每一步都从可选的位置(包括棋盘外部的)中独立随机地选择一个进行移动,直到移动了 K 次或跳到了棋盘外面。
求移动结束后,“马” 仍留在棋盘上的概率。
示例:
输入: 3, 2, 0, 0
输出: 0.0625
解释:
输入的数据依次为 N, K, r, c
第 1 步时,有且只有 2 种走法令 “马” 可以留在棋盘上(跳到(1,2)或(2,1))。对于以上的两种情况,各自在第2步均有且只有2种走法令 “马” 仍然留在棋盘上。
所以 “马” 在结束后仍在棋盘上的概率为 0.0625。
注意:
N 的取值范围为 [1, 25]
K 的取值范围为 [0, 100]
开始时,“马” 总是位于棋盘上
- 1、这题目暂时没有看懂,先下个坑,抄个答案。。。
class Solution {
public:
int d[8][2] = {
{1, 2}, {2, 1}, {2, - 1}, {1, -2},
{-1, -2}, {-2, -1}, {-2, 1}, {-1, 2}
};
double knightProbability(int N, int K, int r, int c) {
if (K == 0) return 1.0;
vector<vector<double> > dp(N, vector<double>(N, 1));
for (int k = 1; k <= K; ++k) {
auto ans = dp;
for (int i = 0; i < N; ++i) {
for (int j = 0; j < N; ++j) {
ans[i][j] = 0;
for (int l = 0; l < 8; ++l) {
int r = i + d[l][0];
int c = j + d[l][1];
if (r >= 0 && r < N && c >= 0 && c < N) {
ans[i][j] += dp[r][c] / 8;
}
}
}
}
swap(ans, dp);
}
return dp[r][c];
}
};
时间复杂度:O(K * N)
空间复杂度:O(r * c)
class Solution { public double knightProbability(int N, int K, int sr, int sc) { double[][] dp = new double[N][N]; int[] dr = new int[]{2, 2, 1, 1, -1, -1, -2, -2}; int[] dc = new int[]{1, -1, 2, -2, 2, -2, 1, -1};
dp[sr][sc] = 1;
for (; K > 0; K--) {
double[][] dp2 = new double[N][N];
for (int r = 0; r < N; r++) {
for (int c = 0; c < N; c++) {
for (int k = 0; k < 8; k++) {
int cr = r + dr[k];
int cc = c + dc[k];
if (0 <= cr && cr < N && 0 <= cc && cc < N) {
dp2[cr][cc] += dp[r][c] / 8.0;
}
}
}
}
dp = dp2;
}
double ans = 0.0;
for (double[] row: dp) {
for (double x: row) ans += x;
}
return ans;
}
}
class Solution:
def knightProbability(self, n: int, k: int, row: int, column: int) -> float:
# 0状态时候的dp
cur_graph = [[0] * n for _ in range(n)]
cur_graph[row][column] = 1
for _ in range(k):
next_graph = [[0] * n for _ in range(n)]
for i in range(n):
for j in range(n):
# 以上一步的cur[i][j]为起点,向八个方向都可以走的,每个方向都是1/8的概率
for x, y in ((i-1, j-2), (i-1, j+2), (i+1, j-2), (i+1, j+2), (i-2, j-1), (i-2, j+1), (i+2, j-1), (i+2, j+1)):
if 0 <= x < n and 0 <= y < n:
next_graph[x][y] += cur_graph[i][j] / 8
cur_graph = next_graph
return sum([sum(l) for l in cur_graph])
动态规划
class Solution {
public double knightProbability(int N, int K, int sr, int sc) {
double[][] dp = new double[N][N];
int[] dr = new int[]{2, 2, 1, 1, -1, -1, -2, -2};
int[] dc = new int[]{1, -1, 2, -2, 2, -2, 1, -1};
dp[sr][sc] = 1;
for (; K > 0; K--) {
double[][] dp2 = new double[N][N];
for (int r = 0; r < N; r++) {
for (int c = 0; c < N; c++) {
for (int k = 0; k < 8; k++) {
int cr = r + dr[k];
int cc = c + dc[k];
if (0 <= cr && cr < N && 0 <= cc && cc < N) {
dp2[cr][cc] += dp[r][c] / 8.0;
}
}
}
}
dp = dp2;
}
double ans = 0.0;
for (double[] row: dp) {
for (double x: row) ans += x;
}
return ans;
}
}
动态规划
class Solution {
private int[][] dir = {{-1, -2}, {1, -2}, {2, -1}, {2, 1}, {1, 2}, {-1, 2}, {-2, 1}, {-2, -1}};
public double knightProbability(int N, int K, int r, int c) {
double[][] dp = new double[N][N];
dp[r][c] = 1;
for (int step = 1; step <= K; step++) {
double[][] dpTemp = new double[N][N];
for (int i = 0; i < N; i++)
for (int j = 0; j < N; j++)
for (int[] direction : dir) {
int lastR = i - direction[0];
int lastC = j - direction[1];
if (lastR >= 0 && lastR < N && lastC >= 0 && lastC < N)
dpTemp[i][j] += dp[lastR][lastC] * 0.125;
}
dp = dpTemp;
}
double res = 0;
for (int i = 0; i < N; i++)
for (int j = 0; j < N; j++)
res += dp[i][j];
return res;
}
}
复杂度
设:棋盘为NxNNxN
// 11-7 DP
class Solution {
public:
double knightProbability(int n, int k, int row, int column) {
// 马每次移动有8种可能 对应着每次的概率为1/8
vector<pair<int, int>> dir = {{1,2},{1,-2},{2,1},{2,-1},{-1,2},{-1,-2},{-2,1},{-2,-1}};
vector<vector<double>> dp(n, vector<double> (n, 1));
dp[row][column] = 1;
for (int i = 0; i < k; i++) {
vector<vector<double>> tmp = dp;
for (int x = 0; x < n; x++) {
for (int y = 0; y < n; y++) {
tmp[x][y] = 0;
for (auto m : dir) {
int dx = x + m.first;
int dy = y + m.second;
if (dx >= 0 && dx < n && dy >= 0 && dy < n) {
tmp[x][y] += dp[dx][dy] / 8;
}
}
}
}
dp = tmp;
}
return dp[row][column];
}
};
class Solution(object):
def knightProbability(self, N, K, r, c):
dp = [[0] * N for _ in xrange(N)]
dp[r][c] = 1
for _ in xrange(K):
dp2 = [[0] * N for _ in xrange(N)]
for r, row in enumerate(dp):
for c, val in enumerate(row):
for dr, dc in ((2,1),(2,-1),(-2,1),(-2,-1),
(1,2),(1,-2),(-1,2),(-1,-2)):
if 0 <= r + dr < N and 0 <= c + dc < N:
dp2[r+dr][c+dc] += val / 8.0
dp = dp2
return sum(map(sum, dp))
double knightProbability(int n, int k, int row, int column) {
vector<vector<double>> dp = vector<vector<double>>(n, vector<double>(n));
vector<int> dr = { 1, 1, -1, -1, 2, 2, -2, -2 };
vector<int> dc = { 2, -2, 2, -2, 1, -1, 1, -1 };
dp[row][column] = 1;
for (int i = k; i > 0; --i) {
vector<vector<double>> dp2 = vector<vector<double>>(n, vector<double>(n));
for (int r = 0; r < n; ++r) {
for (int c = 0; c < n; ++c) {
for (int j = 0; j < 8; j++) {
int cr = r + dr[j];
int cc = c + dc[j];
if (0 <= cr && cr < n && cc >= 0 && cc < n) {
dp2[cr][cc] += dp[r][c] / 8.0;
}
}
}
}
dp = dp2;
}
double ans = 0;
for (int i = 0; i < n; ++i){
for (int j = 0; j < n; ++j){
ans += dp[i][j];
}
}
return ans;
}
class Solution {
public double knightProbability(int N, int K, int r, int c) {
int[] dx = {1, 1, 2, 2, -1, -1, -2, -2};
int[] dy = {2, -2, 1, -1, 2, -2, 1, -1};
double[][] cur = new double[N][N];
cur[r][c] = 1;
for(int k = 0; k < K; k++){
double[][] nxt = new double[N][N];
for(int x = 0; x < N; x++){
for(int y = 0; y < N; y++){
for(int i = 0; i < 8; i++){
int newX = x + dx[i], newY = y + dy[i];
if(newX >= 0 && newX < N && newY >= 0 && newY < N){
nxt[newX][newY] += cur[x][y] / 8.0;
}
}
}
}
cur = nxt;
}
double res = 0.0;
for(double[] row : cur){
for(double p : row){
res += p;
}
}
return res;
}
}
class Solution(object):
def knightProbability(self, N, K, r, c):
dp = [[0] * N for _ in range(N)]
dp[r][c] = 1
for _ in range(K):
dp2 = [[0] * N for _ in range(N)]
for r, row in enumerate(dp):
for c, val in enumerate(row):
for dr, dc in ((2,1),(2,-1),(-2,1),(-2,-1),
(1,2),(1,-2),(-1,2),(-1,-2)):
if 0 <= r + dr < N and 0 <= c + dc < N:
dp2[r+dr][c+dc] += val / 8.0
dp = dp2
return sum(map(sum, dp))
class Solution {
private int[][] dir = {{-1, -2}, {1, -2}, {2, -1}, {2, 1}, {1, 2}, {-1, 2}, {-2, 1}, {-2, -1}};
public double knightProbability(int N, int K, int r, int c) {
double[][] dp = new double[N][N];
dp[r][c] = 1;
for (int step = 1; step <= K; step++) {
double[][] dpTemp = new double[N][N];
for (int i = 0; i < N; i++)
for (int j = 0; j < N; j++)
for (int[] direction : dir) {
int lastR = i - direction[0];
int lastC = j - direction[1];
if (lastR >= 0 && lastR < N && lastC >= 0 && lastC < N)
dpTemp[i][j] += dp[lastR][lastC] * 0.125;
}
dp = dpTemp;
}
double res = 0;
for (int i = 0; i < N; i++)
for (int j = 0; j < N; j++)
res += dp[i][j];
return res;
}
}
class Solution:
def knightProbability(self, N: int, K: int, r: int, c: int) -> float:
dp = [[0] * N for _ in range(N)]
dp[r][c] = 1
for _ in range(K):
dp2 = [[0] * N for _ in range(N)]
for r, row in enumerate(dp):
for c, val in enumerate(row):
for dr, dc in ((2, 1), (2, -1), (-2, 1), (-2, -1),
(1, 2), (1, -2), (-1, 2), (-1, -2)):
if 0 <= r + dr < N and 0 <= c + dc < N:
dp2[r + dr][c + dc] += val / 8.0
dp = dp2
return sum(map(sum, dp))
利用dp去计算k次运算后每个格子会出现骑士的次数, 最后生成的dp矩阵会保存所有骑士的occurance 加起来的总和除以总概率空间 8^k k个步骤每次都有8个走法 就有了8^k种可能
class Solution:
def knightProbability(self, n: int, k: int, row: int, column: int) -> float:
diag_moves = [[1,2],[1,-2],[-1,2],[-1,-2],[2,1],[-2,1],[2,-1],[-2,-1]]
dp= [[0]* n for _ in range(n)]
dp[row][column] = 1
for move in range(k):
temp_dp =[[0]* n for _ in range(n)]
for i in range(n):
for j in range(n):
for dx,dy in diag_moves:
new_x,new_y = i+dx,j+dy
if(new_x<0 or new_x>=n or new_y<0 or new_y>=n): continue
temp_dp[new_x][new_y] += dp[i][j]
dp = temp_dp
res = 0.0
for i in range(n):
res+= sum(dp[i])
return res/pow(len(diag_moves),k)
今天生病了,先借大佬的答案插个眼
double f[25][25][101]; /* dp[i][j][k]: 跳k 步后到达格子(i,j), 此时继续按"日"字向前跳, 跳到大K步时留在棋盘上的概率之和(走法的总概率)。 */
class Solution {
public:
double knightProbability(int N, int K, int r, int c) {
memset(f, 0.0, sizeof(f));
for (int i = 0; i < N; i++)
for (int j = 0; j < N; j++)
f[i][j][K] = 1; /* 预处理边界 */
int dx[] = {-2,-1,1,2,2,1,-1,-2}; /* 8组方向向量 */
int dy[] = {1,2,2,1,-1,-2,-2,-1};
for (int k = K - 1; k >= 0; k--)
for (int i = 0; i < N; i++)
for (int j = 0; j < N; j++)
for (int u = 0; u < 8; u++) /* 枚举8个方向, 累加其中合法方向的概率 */
{
int x = i + dx[u], y = j + dy[u];
if (x >= 0 && x < N && y >= 0 && y < N)
f[i][j][k] += f[x][y][k+1] / 8;
}
return f[r][c][0];
}
};
Time:O(kN^2) Space:O(kN^2)
走完K步后填满一个棋盘,每一格表示走完k步后停留在此的概率,累加所有格子的概率即为停留在棋盘的概率
class Solution:
def knightProbability(self, n: int, k: int, row: int, column: int) -> float:
dp = [[0] * n for _ in range(n)] #最终填满的数组表示的是走完k步后停留在[x][y]位置的概率,全部加起来就是停留在棋盘的概率
dp[row][column] = 1 #初始
for i in range(k): #走k次,每次都算一遍停留在每个位置的概率并累加
dpTemp = [[0]*n for _ in range(n)]
for x in range(n):
for y in range(n):
for stepx, stepy in ((1,2),(1,-2),(-1,2),(-1,-2),(2,1),(2,-1),(-2,1),(-2,-1)):
if 0 <= x + stepx < n and 0 <= y + stepy < n:
dpTemp[x][y] += 0.125*dp[x+stepx][y+stepy] #不能更新到dp上,会影响其他格子概率的计算
dp = dpTemp #统一更新一轮概率
res = 0
for p in range(n):
for q in range(n):
res += dp[p][q]
return res
'''
class Solution {
public:
double knightProbability(int n, int k, int row, int column) {
vector<vector
/**
* @param {number} n
* @param {number} k
* @param {number} row
* @param {number} column
* @return {number}
*/
var knightProbability = function(N, K, r, c) {
let dir = [[-2, 1], [-1, 2], [1, 2], [2, 1], [2, -1], [1, -2], [-1, -2], [-2, -1]]
let dp=new Array(N)
let dp2=new Array(N)
for (let i=0;i<N;i++){
dp[i]=new Array(N).fill(1)
dp2[i]=new Array(N).fill(0)
}
let lastTable=dp,curTable=dp2
for (let i=1;i<=K;i++){
for (let curR=0;curR<N;curR++){
for (let curC=0;curC<N;curC++){
curTable[curR][curC]=0
for (let [dR,dC] of dir){
if(curR+dR>=0&&curR+dR<N&&curC+dC>=0&&curC+dC<N){
curTable[curR][curC]+=(lastTable[curR+dR][curC+dC]/8)
}
}
}
}
let temp=lastTable
lastTable=curTable
curTable=temp
}
return lastTable[r][c]
}
class Solution:
def knightProbability(self, n: int, k: int, row: int, column: int) -> float:
dp = [[0] * n for _ in range(n)]
dp[row][column] = 1
for _ in range(k):
dp2 = [[0] * n for _ in range(n)]
for r, row in enumerate(dp):
for c, val in enumerate(row):
for dr, dc in ((2,1),(2,-1),(-2,1),(-2,-1),
(1,2),(1,-2),(-1,2),(-1,-2)):
if 0 <= r + dr < n and 0 <= c + dc < n:
dp2[r+dr][c+dc] += val / 8.0
dp = dp2
return sum(map(sum, dp))
class Solution:
def knightProbability(self, n: int, k: int, row: int, column: int) -> float:
direct = [[-1, -2], [1, -2], [2, -1], [2, 1], [1, 2], [-1, 2], [-2, 1], [-2, -1]]
dp = [[0] * n for _ in range(n)]
dp[row][column] = 1
for _ in range(k):
dp_temp = [[0] * n for _ in range(n)]
for i in range(n):
for j in range(n):
for d in direct:
lastR = i - d[0]
lastC = j - d[1]
if lastR >= 0 and lastR < n and lastC >= 0 and lastC < n:
dp_temp[i][j] += dp[lastR][lastC] * 0.125
dp = dp_temp
res = 0
for i in range(n):
for j in range(n):
res += dp[i][j]
return res
class Solution:
def knightProbability(self, n: int, k: int, row: int, column: int) -> float:
# 0状态时候的dp
cur_graph = [[0] * n for _ in range(n)]
cur_graph[row][column] = 1
for _ in range(k):
next_graph = [[0] * n for _ in range(n)]
for i in range(n):
for j in range(n):
# 以上一步的cur[i][j]为起点,向八个方向都可以走的,每个方向都是1/8的概率
for x, y in ((i-1, j-2), (i-1, j+2), (i+1, j-2), (i+1, j+2), (i-2, j-1), (i-2, j+1), (i+2, j-1), (i+2, j+1)):
if 0 <= x < n and 0 <= y < n:
next_graph[x][y] += cur_graph[i][j] / 8
cur_graph = next_graph
return sum([sum(l) for l in cur_graph])
···
dp 数组
class Solution {
public:
int xx[8]={-2,-2,-1,-1,1,1,2,2};
int yy[8]={-1,1,-2,2,-2,2,-1,1};
double knightProbability(int n, int k, int row, int column) {
int cnt1=0,cnt2=0;
dfs(n,k,row,column,cnt1,cnt2);
return (double)cnt1/(cnt1+cnt2);
}
void dfs(int n,int k,int x,int y,int &cnt1,int &cnt2){
if(k==0){
cnt1++;
return;
}
for(int i=0;i<8;i++){
int dx=xx[i]+x,dy=yy[i]+y;
if(dx<0||dx>=n||dy<0||dy>=n){
cnt2++;
continue;
}
dfs(n,k-1,dx,dy,cnt1,cnt2);
}
}
};
代码 python class Solution: def knightProbability(self, n: int, k: int, row: int, column: int) -> float: direct = [[-1, -2], [1, -2], [2, -1], [2, 1], [1, 2], [-1, 2], [-2, 1], [-2, -1]]
dp = [[0] * n for _ in range(n)]
dp[row][column] = 1
for _ in range(k):
dp_temp = [[0] * n for _ in range(n)]
for i in range(n):
for j in range(n):
for d in direct:
lastR = i - d[0]
lastC = j - d[1]
if lastR >= 0 and lastR < n and lastC >= 0 and lastC < n:
dp_temp[i][j] += dp[lastR][lastC] * 0.125
dp = dp_temp
res = 0
for i in range(n):
for j in range(n):
res += dp[i][j]
return res
复杂度 时间: O(kn^2) 空间: O(kn^2)
class Solution {
public double knightProbability(int n, int k, int row, int column) {
int[] dRow = new int[]{2, 2, -2, -2, 1, 1, -1, -1};
int[] dCol = new int[]{1, -1, 1, -1, 2, -2, 2, -2};
double[][] dp = new double[n][n];
int curRow = row;
int curCol = column;
dp[row][column] = 1.0;
for (int i = 1; i <= k; i++) {
double[][] dp2 = new double[n][n];
for (int r = 0; r < n; r++) {
for (int c = 0; c < n; c++) {
for (int j = 0; j < 8; j++) {
curRow = r + dRow[j];
curCol = c + dCol[j];
if (curRow >= 0 && curRow < n && curCol >= 0 && curCol < n) {
dp2[curRow][curCol] += dp[r][c] / 8.0;
//System.out.println(i + " " + dp[r][c] + " " + dp2[curRow][curCol]);
}
}
}
}
dp = dp2;
}
double res = 0.0;
for (double[] arr : dp) {
for (double prob : arr) {
//System.out.println(prob);
res += prob;
}
}
return res;
}
}
mark下 还没看懂
/**
* @param {number} N
* @param {number} K
* @param {number} r
* @param {number} c
* @return {number}
*/
var knightProbability = function(N, K, r, c) {
// 每一个点可以移动的位置
const MOVE = [
[2, 1],
[2, -1],
[-2, 1],
[-2, -1],
[1, 2],
[1, -2],
[-1, 2],
[-1, -2],
];
let res = 0;
// dp[i][j] = 落在 i j 的概率
let dp = Array.from({ length: N }).map(() => new Array(N).fill(0));
dp[r][c] = 1;
for (let s = 0; s < K; s++) {
const tempDp = Array.from({ length: N }).map(() => new Array(N).fill(0));
for (let i = 0; i < N; i++) {
for (let j = 0; j < N; j++) {
for (let item of MOVE) {
const tempR = i + item[0];
const tempC = j + item[1];
if (tempR >= 0 && tempR < N && tempC >= 0 && tempC < N) {
// 表明该方向还是在棋盘中
tempDp[i][j] += dp[tempR][tempC] * 0.125;
}
}
}
}
dp = tempDp;
}
for (let i = 0; i < N; i++) {
for (let j = 0; j < N; j++) {
res += dp[i][j];
}
}
return res;
};
class Solution {
public:
double knightProbability(int N, int K, int r, int c) {
if (N == 0) { return 0; }
vector<vector<vector<double>>> dp(N + 4, vector<vector<double>>(N + 4, vector<double>(K + 1)));
for (int i = 0; i < N + 4; i++) {
for (int j = 0; j < N + 4; j++) {
if ((i) >= 2 && (i) <= N + 1) {
if ((j)>= 2 && (j) <= N + 1) {
dp[i][j][0] = 1;
}
}
else {
dp[i][j][0] = 0;
}
}
}
for (int k = 1; k <= K; k++) {
for (int i = 2; i <= N + 1; i++) {
for (int j = 2; j <= N + 1; j++) {
dp[i][j][k] = (dp[i - 2][j - 1][k - 1] + dp[i - 2][j + 1][k - 1] + \
dp[i - 1][j - 2][k - 1] + dp[i - 1][j + 2][k - 1] + \
dp[i + 1][j - 2][k - 1] + dp[i + 1][j + 2][k - 1] + \
dp[i + 2][j - 1][k - 1] + dp[i + 2][j + 1][k - 1]) / 8.0;
}
}
}
return dp[r + 2][c + 2][K];
}
};
class Solution(object):
def knightProbability(self, N, K, r, c):
dp = [[0] * N for _ in xrange(N)]
dp[r][c] = 1
for _ in xrange(K):
dp2 = [[0] * N for _ in xrange(N)]
for r, row in enumerate(dp):
for c, val in enumerate(row):
for dr, dc in ((2,1),(2,-1),(-2,1),(-2,-1),
(1,2),(1,-2),(-1,2),(-1,-2)):
if 0 <= r + dr < N and 0 <= c + dc < N:
dp2[r+dr][c+dc] += val / 8.0
dp = dp2
return sum(map(sum, dp))
https://leetcode-cn.com/problems/knight-probability-in-chessboard/
DFS,DP
class Solution:
def knightProbability(self, n: int, k: int, row: int, column: int) -> float:
# time 8**k
# space n*n
# dfs
# @lru_cache(None)
def dfs(x, y, counts):
if (x, y, counts) in memo:
return memo[(x, y, counts)]
if counts == k:
return 1
valid = 0
total = 0
for newX, newY in [(x + 2, y + 1), (x + 2, y - 1), (x - 1, y + 2), (x + 1, y + 2), (x - 2, y + 1), (x - 2, y - 1), (x + 1, y - 2), (x - 1, y - 2)]:
total += 1
if 0 <= newX < n and 0 <= newY < n:
valid += dfs(newX, newY, counts + 1)
memo[(x, y, counts)] = valid / total
return memo[(x, y, counts)]
memo = dict()
return dfs(row, column, 0)
思路:主要使用的是动态规划,如果当前位置的概率,会影响该位置的8个方向的概率,dp2[cr] [cc] += dp[r] [c] / 8.0;(该题抄的答案,读了二遍题没有看出来是动态规格)
class Solution { public double knightProbability(int N, int K, int sr, int sc) { double[][] dp = new double[N][N]; int[] dr = new int[]{2, 2, 1, 1, -1, -1, -2, -2}; int[] dc = new int[]{1, -1, 2, -2, 2, -2, 1, -1}; dp[sr][sc] = 1; for (; K > 0; K--) { double[][] dp2 = new double[N][N]; for (int r = 0; r < N; r++) { for (int c = 0; c < N; c++) { for (int k = 0; k < 8; k++) { int cr = r + dr[k]; int cc = c + dc[k]; if (0 <= cr && cr < N && 0 <= cc && cc < N) { dp2[cr][cc] += dp[r][c] / 8.0; } } } } dp = dp2; } double ans = 0.0; for (double[] row: dp) { for (double x: row) ans += x; } return ans; } }
class Solution {
public double knightProbability(int N, int K, int sr, int sc) {
double[][] dp = new double[N][N];
int[] dr = new int[]{2, 2, 1, 1, -1, -1, -2, -2};
int[] dc = new int[]{1, -1, 2, -2, 2, -2, 1, -1};
dp[sr][sc] = 1;
for (; K > 0; K--) {
double[][] dp2 = new double[N][N];
for (int r = 0; r < N; r++) {
for (int c = 0; c < N; c++) {
for (int k = 0; k < 8; k++) {
int cr = r + dr[k];
int cc = c + dc[k];
if (0 <= cr && cr < N && 0 <= cc && cc < N) {
dp2[cr][cc] += dp[r][c] / 8.0;
}
}
}
}
dp = dp2;
}
double ans = 0.0;
for (double[] row: dp) {
for (double x: row) ans += x;
}
return ans;
}
}
688. “马”在棋盘上的概率
入选理由
暂无
题目地址
https://leetcode-cn.com/problems/knight-probability-in-chessboard/
前置知识
题目描述
现有一个 “马”(也译作 “骑士”)位于 (r, c) ,并打算进行 K 次移动。
如下图所示,国际象棋的 “马” 每一步先沿水平或垂直方向移动 2 个格子,然后向与之相垂直的方向再移动 1 个格子,共有 8 个可选的位置。
现在 “马” 每一步都从可选的位置(包括棋盘外部的)中独立随机地选择一个进行移动,直到移动了 K 次或跳到了棋盘外面。
求移动结束后,“马” 仍留在棋盘上的概率。