刷题的日常-网格中的最小路径代价

168 阅读2分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第24天,点击查看活动详情

刷题的日常

一天一题,保持脑子清爽

网格中的最小路径代价

今天的题来自leetcode的2304题,题意如下:

给定一个矩阵grip,下标从0开始,矩阵大小为 m x n ,由 0 到 m * n - 1 的不同整数组成。你可以在此矩阵中,从一个单元格移动到 下一行 的任何其他单元格。如果你位于单元格 (x, y) ,且满足 x < m - 1 ,你可以移动到 (x + 1, 0), (x + 1, 1), ..., (x + 1, n - 1) 中的任何一个单元格。在最后一行中的单元格不能触发移动。

每次可能的移动都需要付出对应的代价,代价用一个下标从 0 开始的二维数组 moveCost 表示,该数组大小为 (m * n) x n ,其中 moveCost[i][j] 是从值为 i 的单元格移动到下一行第 j 列单元格的代价。从 grid 最后一行的单元格移动的代价可以忽略。

grid 一条路径的代价是:所有路径经过的单元格的 值之和 加上 所有移动的 代价之和 。从 第一行 任意单元格出发,返回到达 最后一行 任意单元格的最小路径代价。

审题

  • 其实就是从第一行开始往下走,到达最后一行时代价最小的路径
  • 最终的代价是由 路径经过的节点和路径上的代价总和

暴力解

  • 直接枚举所有的路径,将所有的节点和路径代价加起来,最后取代价最低的结果返回即可。
  • 时间复杂度为O(m ^ n),直接暴力会导致超时,所以得另寻它法。

动态规划

由题意可以看出,我们想到达下一行的某个节点,节点的最小代价为 当前行的所有节点的最小代价 + 到达下一行某个节点的代价取最小值,所以可以用动态规划将没必要的搜索省下。时间复杂度为O(m * (n ^ 2))。

public class Solution {
    public int minPathCost(int[][] grid, int[][] moveCost) {
        int[][] dp = new int[2][grid[0].length];
        System.arraycopy(grid[0], 0, dp[0], 0, dp[0].length);
        byte r = 1;
        for (int i = 1; i < grid.length; i++) {
            int[] pre = grid[i - 1];
            for (int j = 0; j < pre.length; j++) {
                dp[r][j] = Integer.MAX_VALUE;
                for (int z = 0; z < pre.length; z++) {
                    dp[r][j] = Math.min(dp[r][j], moveCost[pre[z]][j] + dp[r ^ 1][z] + grid[i][j]);
                }
            }
            r ^= 1;
        }
        r ^= 1;
        int result = Integer.MAX_VALUE;
        for (int item : dp[r]) {
            result = Math.min(item, result);
        }
        return result;
    }
}