采用动态规划的思想来解决:题意中可以看出,数组的每一行取一个数,因此下一行的状态可以通过上一行的值来预测,具体分析如下:
如图所示为一个4*4的矩阵,我们定义
d
p
[
i
]
[
j
]
dp[i][j]
dp[i][j]来表示选取矩阵中第i行第j列的数作为最后一个数,从而得到的非零偏移下降矩阵的最小值,则可知:
当i为0时,表示矩阵第一行, d p [ 0 ] [ j ] dp[0][j] dp[0][j]表示选取矩阵中第 0 0 0行第 j j j列的数作为最后一个数,则可知:
d p [ 0 ] [ j ] = g r i d [ 0 ] [ j ] dp[0][j] = grid[0][j] dp[0][j]=grid[0][j]
即
d
p
[
0
]
dp[0]
dp[0]的值为:
根据
d
p
[
0
]
dp[0]
dp[0]来寻找
d
p
[
1
]
dp[1]
dp[1],则
d
p
[
1
]
[
0
]
dp[1][0]
dp[1][0]表示第一行的数字取
g
r
i
d
[
1
]
[
0
]
grid[1][0]
grid[1][0]的非零偏移下降矩阵的最小值,则可知:
d p [ 1 ] [ 0 ] = m i n ( d p [ 0 ] [ 1 ] , d p [ 0 ] [ 2 ] , d p [ 0 ] [ 3 ] ) + g r i d [ 1 ] [ 0 ] dp[1][0] = min(dp[0][1],dp[0][2],dp[0][3])+grid[1][0] dp[1][0]=min(dp[0][1],dp[0][2],dp[0][3])+grid[1][0]
其他也可按照同理进行推断,总公式为:
d p [ 1 ] [ i ] = m i n ( d p [ 0 ] [ j ] ) + g r i d [ 1 ] [ i ] , j = 0 , 1 , . . . , n 且 j ! = i dp[1][i] = min(dp[0][j])+grid[1][i],\ j=0,1,...,n\ 且\ j!=i dp[1][i]=min(dp[0][j])+grid[1][i], j=0,1,...,n 且 j!=i
即
d
p
[
1
]
dp[1]
dp[1]的值为:
则可得递推公式为:
d p [ i ] [ j ] = m i n ( d p [ i − 1 ] [ k ] ) + g r i d [ i ] [ j ] , k ! = j dp[i][j] =min(dp[i-1][k])+grid[i][j]\ ,k!=j dp[i][j]=min(dp[i−1][k])+grid[i][j] ,k!=j
最后,遍历
d
p
[
n
−
1
]
dp[n-1]
dp[n−1]找到最小值即可。
代码如下:
class Solution {
public int minFallingPathSum(int[][] grid) {
int n = grid.length;
int[][] dp = new int[n][n];
for(int i=0;i<n;i++){
dp[0][i] = grid[0][i];
}
int row = 1, column = 0;
while (row<n){
while (column<n){
int laxmin = Integer.MAX_VALUE;
for(int i=0;i<n;i++){
if(i==column) continue;
laxmin = Math.min(laxmin,dp[row-1][i]);
}
dp[row][column] = laxmin + grid[row][column];
column++;
}
System.out.println(" ");
row++;
column = 0;
}
int min = Integer.MAX_VALUE;
for(int i=0;i<n;i++){
min = Math.min(min,dp[n-1][i]);
}
return min;
}
此代码的时间复杂度为 n 3 n^3 n3。由于在遍历时进行了重复遍历,每次寻找某行最小值时都需要遍历一遍上一行的值,增加了时间复杂度,可以先行记录下上一行的最小值 f i r s t m i n firstmin firstmin,最小值索引 f i r s t i n d e x firstindex firstindex以及次小值 s e c o n d m i n secondmin secondmin,则递推公式变为:
d p [ i ] [ j ] = { f i r s t m i n + g r i d [ i ] [ j ] , j ! = f i r s t i n d e x , s e c o n d m i n + g r i d [ i ] [ j ] , j = f i r s t i n d e x dp[i][j] =\left \{ \begin{array}{c} firstmin+grid[i][j],j!=firstindex, \\ \\ secondmin+grid[i][j],j=firstindex \end{array} \right. dp[i][j]=⎩ ⎨ ⎧firstmin+grid[i][j],j!=firstindex,secondmin+grid[i][j],j=firstindex
即在每次寻找完 d p [ i ] dp[i] dp[i]之后,更新三个值即可,此时可使时间复杂度降为 n 2 n^2 n2,优化之后的代码如下:
class Solution {
public int minFallingPathSum(int[][] grid) {
int n = grid.length;
int[][] dp = new int[n][n];
for(int i=0;i<n;i++){
dp[0][i] = grid[0][i];
}
int[] calcutate = findNumber(dp[0]);
System.out.println(calcutate[0]);
System.out.println(calcutate[2]);
int length = 1, high = 0;
while (length<n){
while (high<n){
if(high == calcutate[1]) dp[length][high] = calcutate[2] + grid[length][high];
else dp[length][high] = calcutate[0] + grid[length][high];
high++;
}
calcutate = findNumber(dp[length]);
length++;
high = 0;
}
return calcutate[0];
}
public int[] findNumber(int[] dp){
int firstindex = -1;
int firstmin = Integer.MAX_VALUE;
int secondmin = Integer.MAX_VALUE;
for(int i=0;i<dp.length;i++){
if(secondmin>dp[i]){
secondmin = dp[i];
if(firstmin>secondmin){
int temp = firstmin;
firstmin = secondmin;
firstindex = i;
secondmin = temp;
}
}
}
return new int[]{firstmin,firstindex,secondmin};
}
}
~~~