问题描述:
给定一个矩阵matrix,先从左上角开始,每一步只能往右或者往下走,走到右下角。然后从右下角出发,每一步只能往上或者往左走,再回到左上角。任何一个位置的数字,只能获得一遍。返回最大路径和。
思路:
思路一:暴力递归方法。此题可以转化为两个人A和B同时从左上角位置开始走,并且每一步只能往右或者往下走,走到右下角,A和B一定迈出相同的步数同步走。
我们从转化后的问题可以得知:
- A和B会同时来到右下角的位置。
- A所在行号用Ar表示,列号用Ac表示;B所在行号用Br表示,列号用Bc表示;四个变量之前存在一定的关系:Ar + Ac = Br + Bc,所以我门在递归函数中可以省去一个变量Bc (Bc = Ar + Ac - Br)。
- 不同时刻,A所在数组的第i行和第j列,B在第i行时不在第j列上,则B之后不会经过第i行和第j列这个位置。
- 某一时刻,A和B都是所在数组的第i行,那么A和B一定在同一个格子里面。
递归函数表示的含义是:两个人同时到达右下角,返回两个人路径的最大累加和。
思路二:使用记忆化搜索的方法,建立缓存数组。
代码:
思路一代码
public static int comeGoMaxPathSum(int[][] matrix) {
return process(matrix, 0, 0, 0);
}
//matrix中没有负数
//A和B,一定迈出相同的步数,同步走
//两个人会共同到达右下角,返回两个人路径的最大累加和
public static int process(int[][] matrix, int Ar, int Ac, int Br) {
int N = matrix.length;
int M = matrix[0].length;
if (Ar == N - 1 && Ac == M - 1) {
return matrix[Ar][Ac];
}
//没有到达右下角
// A 下 B 右
// A 下 B 下
// A 右 B 右
// A 右 B 下
int Bc = Ar + Ac - Br;
int ADownBRight = 0;
if (Ar + 1 < N && Bc + 1 < M) {
ADownBRight = process(matrix, Ar + 1, Ac, Br);
}
int ADownBDown = 0;
if (Ar + 1 < N && Br + 1 < N) {
ADownBDown = process(matrix, Ar + 1, Ac, Br + 1);
}
int ARightBRight = 0;
if (Ac + 1 < M && Bc + 1 < M) {
ARightBRight = process(matrix, Ar, Ac + 1, Br);
}
int ARightBDown = 0;
if (Ac + 1 < M && Br + 1 < N) {
ARightBDown = process(matrix, Ar, Ac + 1, Br + 1);
}
int nextBest = Math.max(Math.max(ADownBRight, ADownBDown), Math.max(ARightBRight, ARightBDown));
int curNum = Ar == Br ? matrix[Ar][Ac] : matrix[Ar][Ac] + matrix[Br][Bc];
int res = curNum + nextBest;
return res;
}
思路二代码
public static int dp(int[][] matrix) {
int M = matrix.length;
int N = matrix[0].length;
int[][][] dp = new int[M][N][M];
//最后一个位置
dp[M - 1][N - 1][M - 1] = matrix[M - 1][N - 1];
//普通位置
for (int i = 0; i < M; i++) {
for (int j = 0; j < N; j++) {
for (int k = 0; k < M; k++) {
dp[i][j][k] = Integer.MIN_VALUE;
}
}
}
return process2(matrix, 0, 0, 0, dp);
}
public static int process2(int[][] matrix, int Ar, int Ac, int Br, int[][][] dp) {
if (dp[Ar][Ac][Br] != Integer.MIN_VALUE) {
return dp[Ar][Ac][Br];
}
int N = matrix.length;
int M = matrix[0].length;
if (Ar == N - 1 && Ac == M - 1) {
//A B来到右下角
return matrix[N - 1][M - 1];
}
//没有到达右下角
// A 下 B 右
// A 下 B 下
// A 右 B 右
// A 右 B 下
int Bc = Ar + Ac - Br;
int ADownBRight = 0;
if (Ar + 1 < N && Bc + 1 < M) {
ADownBRight = process2(matrix, Ar + 1, Ac, Br, dp);
}
int ADownBDown = 0;
if (Ar + 1 < N && Br + 1 < N) {
ADownBDown = process2(matrix, Ar + 1, Ac, Br + 1, dp);
}
int ARightBRight = 0;
if (Ac + 1 < M && Bc + 1 < M) {
ARightBRight = process2(matrix, Ar, Ac + 1, Br, dp);
}
int ARightBDown = 0;
if (Ac + 1 < M && Br + 1 < N) {
ARightBDown = process2(matrix, Ar, Ac + 1, Br + 1, dp);
}
int nextBest = Math.max(Math.max(ADownBRight, ADownBDown), Math.max(ARightBRight, ARightBDown));
int curNum = Ar == Br ? matrix[Ar][Ac] : matrix[Ar][Ac] + matrix[Br][Bc];
int res = curNum + nextBest;
dp[Ar][Ac][Br] = res;
return res;
}
public static void main(String[] args) {
int[][] matrix = {
{1, 1, 1, 1, 1, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 1, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 1, 0, 0, 0, 0, 1},
{1, 0, 0, 0, 1, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 1, 1, 1, 1, 1, 1}
};
System.out.println(comeGoMaxPathSum(matrix));
System.out.println(dp(matrix));
}