题目描述
【带传送阵的矩阵游离】
n 行 m 列的矩阵,每个位置上有一个元素。
你可以上下左右行走,代价是前后两个位置元素值差的绝对值。
另外,你最多可以使用一次传送阵(只能从一个数跳到另外一个相同的数),
求从走上角走到右下角最少需要多少时间。
解题思路
这是一个典型的动态规划问题。
我们可以定义一个二维数组 dp,其中 dp[i][j] 表示从左上角走到 (i, j) 位置的最小代价。初始时,dp[0][0] 的值为 0。
然后,我们考虑转移。从左上角到 (i, j) 可以有四种方式:从 (i-1, j) 向下走、从 (i, j-1) 向右走、从 (i+1, j) 向上走、从 (i, j+1) 向左走。但是,这里需要注意的是,如果我们使用了传送阵,那么从一个数跳到另外一个相同的数代价为 0,因此我们需要特殊处理这种情况。
具体地,我们可以枚举每一个位置 (i, j),对于每一个位置,我们计算出四种方式到达该位置的最小代价。如果存在传送阵,我们可以尝试使用传送阵到达该位置,这样可以省去一些代价。然后,从这四种方式中选择代价最小的一种作为从左上角到该位置的最小代价。最后,dp[n-1][m-1] 就是从左上角走到右下角的最小代价。
时间复杂度为 O(nm^2),空间复杂度为 O(nm)。
参考代码
public static int minCost(int[][] matrix) {
int n = matrix.length, m = matrix[0].length;
int[][] dp = new int[n][m];
for (int i = 0; i < n; i++) {
Arrays.fill(dp[i], Integer.MAX_VALUE);
}
dp[0][0] = 0;
Map<Integer, int[]> teleport = new HashMap<>();
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
int[] pos = new int[]{i, j};
teleport.put(matrix[i][j], pos);
if (i > 0) {
int cost = Math.abs(matrix[i][j] - matrix[i-1][j]);
dp[i][j] = Math.min(dp[i][j], dp[i-1][j] + cost);
}
if (j > 0) {
int cost = Math.abs(matrix[i][j] - matrix[i][j-1]);
dp[i][j] = Math.min(dp[i][j], dp[i][j-1] + cost);
}
if (teleport.containsKey(matrix[i][j])) {
int[] prevPos = teleport.get(matrix[i][j]);
if (prevPos[0] != i || prevPos[1] != j) {
int cost = Math.abs(matrix[i][j] - matrix[prevPos[0]][prevPos[1]]);
dp[i][j] = Math.min(dp[i][j], dp[prevPos[0]][prevPos[1]] + cost);
}
}
}
}
return dp[n-1][m-1];
}