记录一下最近在 LeetCode 刷的几道动态规划算法题
Divisor Game 题目来源 链接:https://leetcode.com/problems/divisor-game/ 
难度:easy
题目描述 
给定一个数 N,A、B 两个人轮流取数 x,只能取 (0,N) 之间且为 N 的约数(N 对其求余等于 0),并且取完之后剩余的数为 N-x,从剩余数里面继续取,直到一方没法取值为止,另一方获胜。求给定一个数字 N,问先手的 Alice 是否一定可以获胜。 
 
可以简单举几个例子帮助理解题目:A 先手
N === 0 时,先手的一定输,结果为 false
N === 1 时,先手的也没法选,结果为 false
N === 2 时,先手的只能选择 1,后手输,结果为 true
N === 3 时,先手的只能选择 1,选完以后 N === 2,后手胜,结果为 false
N === 4 时,先手可以选择 1 也可以选择 2
如果选 1,剩 3,相当于问,当 N===3 且 B 先手时,A 是否能赢,前面推出 N === 3 且 A 先手时,A 输,所以当 N===3 且 B 先手时,A 能赢 
如果选 2,剩 2,由前面 N === 2 的结论知,A 会输 
如果 A 先手,A 有先手选择权,所以当 N === 4 时,结果为 true 
 
代码和说明 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 const  divisorGame = function  (N )      const  dp = [false , false , true ];   for  (let  i = 1 ; i <= N; i++) {          if  (dp[i] === undefined ) {              let  currentResult = false ;       for  (let  j = 1 ; j < i; j++) {         if  (i % j === 0 ) {           if  (dp[i - j] === false ) {             currentResult = true ;             break ;           }         }       }       dp[i] = currentResult;     }   }   return  dp[N]; }; 
Matrix Block Sum 题目来源 https://leetcode.com/problems/matrix-block-sum/ 
难度:medium
题目描述 
给定一个 m*n 的矩阵 mat 和一个正整数 K,返回一个 answer 数组,answer[i][j] 是所有 mat[r][c] 之和,r∈[i-K,i+K],c∈[j-K,j+K],并且 r/c 在数组 mat 中都是有效的位置
 
代码和说明 题目求的是矩形区域的面积,可以采用暴力O(n^4) 的方法求解,代码如下:遍历 mat 二维数组,对每个 i/j 计算所有满足条件的 r/c 之和,思路比较简单。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 var  matrixBlockSum = function  (mat, K )         const  answer = new  Array (mat.length).fill(0 ).map((item ) =>  []);   for  (let  i = 0 ; i < mat.length; i++) {     for  (let  j = 0 ; j < mat[i].length; j++) {              let  currentSum = 0 ;       for  (let  r = i - K; r <= i + K; r++) {         for  (let  c = j - K; c <= j + K; c++) {           if  (r >= 0  && r < mat.length && c >= 0  && c < mat[i].length) {             currentSum += mat[r][c];           }         }       }       answer[i][j] = currentSum;     }   }   return  answer; }; 
暴力法重复计算的次数较多,时间复杂度比较高,下面采用了动态规划法对其进行了优化:
矩形之和的求解利用了左边、上面和左上边三个大矩阵的结果,最后实际求解矩阵时,利用 dp 的结果可以很方便的求解出答案
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 var  matrixBlockSum = function  (mat, K )   const  dp = new  Array (mat.length).fill(0 ).map((item ) =>  []);   const  answer = new  Array (mat.length).fill(0 ).map((item ) =>  []);   for  (let  i = 0 ; i < mat.length; i++) {     for  (let  j = 0 ; j < mat[i].length; j++) {       dp[i][j] = mat[i][j];       if  (i - 1  >= 0  && j - 1  >= 0 ) {         dp[i][j] += dp[i - 1 ][j] + dp[i][j - 1 ] - dp[i - 1 ][j - 1 ];       } else  if  (i - 1  >= 0 ) {         dp[i][j] += dp[i - 1 ][j];       } else  if  (j - 1  >= 0 ) {         dp[i][j] += dp[i][j - 1 ];       }     }   }      for  (let  i = 0 ; i < mat.length; i++) {     for  (let  j = 0 ; j < mat[i].length; j++) {              let  rl = i - K < 0  ? 0  : i - K,         rr = i + K > mat.length - 1  ? mat.length - 1  : i + K,         cl = j - K < 0  ? 0  : j - K,         cr = j + K > mat[i].length - 1  ? mat[i].length - 1  : j + K;       answer[i][j] = dp[rr][cr];       if  (cl - 1  >= 0  && rl - 1  >= 0 ) {         answer[i][j] =           answer[i][j] - dp[rr][cl - 1 ] - dp[rl - 1 ][cr] + dp[rl - 1 ][cl - 1 ];       } else  if  (cl - 1  >= 0 ) {         answer[i][j] -= dp[rr][cl - 1 ];       } else  if  (rl - 1  >= 0 ) {         answer[i][j] -= dp[rl - 1 ][cr];       }     }   }   return  answer; }; 
Count Square Submatrices with All Ones 题目来源 https://leetcode.com/problems/count-square-submatrices-with-all-ones/ 
难度:medium
题目描述 
给定一个只包括 0 和 1 的二维数组,返回所有子正方形的个数,正方形不能含 0
1 2 3 4 5 6 7 8 9 10 11 12 13 > Input: matrix = > [ >   [0,1,1,1], >   [1,1,1,1], >   [0,1,1,1] > ] > Output: 15 > Explanation:  > There are 10 squares of side 1. > There are 4 squares of side 2. > There is  1 square of side 3. > Total number of squares = 10 + 4 + 1 = 15. > 
 
1 2 3 4 5 6 7 8 9 10 11 12 > Input: matrix =  > [ >   [1,0,1], >   [1,1,0], >   [1,1,0] > ] > Output: 7 > Explanation:  > There are 6 squares of side 1.   > There is 1 square of side 2.  > Total number of squares = 6 + 1 = 7. > 
 
代码和说明 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 var  countSquares = function  (matrix )   let  result = 0 ;   const  dp = new  Array (matrix.length).fill(0 ).map((item ) =>  []);   for  (let  i = 0 ; i < matrix.length; i++) {     for  (let  j = 0 ; j < matrix[i].length; j++) {       if  (matrix[i][j] === 0 ) {         dp[i][j] = 0 ;       } else  {         if  (i - 1  >= 0  && j - 1  >= 0 ) {                                 if  (             matrix[i - 1 ][j] === 0  ||             matrix[i][j - 1 ] === 0  ||             matrix[i - 1 ][j - 1 ] === 0            ) {             dp[i][j] = 1 ;           } else  {             dp[i][j] =               Math .min(...[dp[i - 1 ][j], dp[i][j - 1 ], dp[i - 1 ][j - 1 ]]) + 1 ;           }         } else  {                      dp[i][j] = 1 ;         }       }       result += dp[i][j];     }   }   return  result; }; 
Minimum Falling Path Sum 题目来源 https://leetcode.com/problems/minimum-falling-path-sum/ 
难度:medium
题目描述 
给定一个 n*n 的二维数组,找到一条从第一行走到最后一行的最短路径。有条件限制:例如当前在第 i行 j 列,走到 i+1 行时,只能去下标合法的 j-1、j 或 j+1 列
 
代码和说明 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 var  minFallingPathSum = function  (A )   const  dp = new  Array (A.length).fill(0 ).map((item ) =>  []);   for  (let  i = 0 ; i < A.length; i++) {     for  (let  j = 0 ; j < A[i].length; j++) {       if  (i === 0 ) {                  dp[i][j] = A[i][j];       } else  {         dp[i][j] = dp[i - 1 ][j];         if  (j - 1  >= 0 ) {           dp[i][j] = Math .min(dp[i][j], dp[i - 1 ][j - 1 ]);         }         if  (j + 1  < A[i].length) {           dp[i][j] = Math .min(dp[i][j], dp[i - 1 ][j + 1 ]);         }         dp[i][j] += A[i][j];       }     }   }   return  Math .min(...dp[dp.length - 1 ]); }; 
Palindromic Substrings 题目来源 https://leetcode.com/problems/palindromic-substrings/ 
难度:medium
题目描述 
给定一个字符串,返回字符串中回文字串的个数
1 2 3 4 > Input: "abc" > Output: 3 > Explanation: Three palindromic strings: "a", "b", "c". > 
 
1 2 3 4 > Input: "aaa" > Output: 6 > Explanation: Six palindromic strings: "a", "a", "a", "aa", "aa", "aaa". > 
 
代码和说明 
有个小问题需要注意,动态规划的 dp 是依赖 dp[i+1][j-1] 的值,所以在计算 dp 前,其依赖的值需要提前被计算,所以可以修改一下遍历方式,斜着遍历:
如:(0,0)、(1,1,)、(2,2)…、(0,1)、(1,2)、(2,3)…、(0,2)、(1,3)、(2,4)…
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 var  countSubstrings = function  (s )   let  result = 0 ;   const  dp = new  Array (s.length).fill(0 ).map((item ) =>  []);      for  (let  step = 0 ; step < s.length; step++) {     for  (let  i = 0 ; i < s.length - step; i++) {       let  j = step + i;              if  (i === j) {         dp[i][j] = true ;         result++;       } else  if  (i + 1  === j) {                  if  (s[i] === s[j]) {           dp[i][j] = true ;           result++;         }       } else  {                  if  (dp[i + 1 ][j - 1 ] && s[i] === s[j]) {           dp[i][j] = true ;           result++;         }       }     }   }   return  result; };