【问题标题】:Finding minimum cost in 2D matrix在二维矩阵中找到最小成本
【发布时间】:2020-08-17 02:07:17
【问题描述】:

在我上次的采访中,有人问我一个问题,我仍然无法弄清楚哪种最佳方法。

给定一个带有n rowsm columns 的二维矩阵,我需要从每一行中选择一项并获得最小总和。

但棘手的部分是,在下降时,我有两个选择:

  1. 选择与上一列相同的元素。
  2. 选择当前列之外的元素

因此,第二个选项,即在下降时更改列可以精确地完成K 次。我们可以重新访问矩阵中的同一列。仅当我们更改当前行和下一行之间的列时才定义更改。

我在面试官面前尝试的是类似Painting wall problem,在给定 3 种类型的油漆时,我需要找到最低成本,这样相邻的两堵墙就不会被漆成相同的颜色。但是那里的 m 值被固定为 3,我可以使用简单的 cost[i][j] = min(cost[i-1][j-1], cost[i-1][j+1]); //min 从当前行中的其他两行。

上面的问题,应该是什么方法,对我来说确实像DP,但我真的不确定它是否会产生结果。

【问题讨论】:

  • @SomeDude 我的意思是,在沿着矩阵向下移动时,我有两个选择:选择同一行中的元素或选择任何其他元素。因此,选择除我当前列之外的任何元素都会更改列。因此,这种下降时的列变化只能发生 K 次。
  • 可以精确地完成 K 次 意味着您“必须”更改 K 次,对吗?不达K次?您选择的下一列也不必是相邻列吗?以及如何重新访问您已经访问过的专栏?我想所有这些都是可能的?
  • @SomeDude 我们需要准确地更改该列K 次,我们可以稍后重新访问同一列。将仅从当前列和下一列定义更改。

标签: algorithm data-structures dynamic-programming


【解决方案1】:

这可以使用动态规划来解决:让dp[i][j][l] 成为我们可以通过从第一行i 中选择一些数字得到的最小总和,最后一个在j th 列中,并且恰好是@ 987654324@ 列更改。最初dp[0][j][0] = matrix[0][j]。对于更新,我们应该使用以下公式:

如果我们选择第一个选项,让我们这样做:
dp[i + 1][j][l] = min(dp[i + 1][j][l], dp[i][j][l] + matrix[i + 1][j])

如果我们选择第二个选项,我们应该为所有 new_col != j 执行此操作:
dp[i + 1][new_col][l + 1] = min(dp[i + 1][new_col][l + 1], dp[i][j][l] + matrix[i + 1][new_col])

结果将是所有jdp[n - 1][j][k] 中的最小值。总体复杂度为
O(n * m^2 * k),因为对于动态 (O(n * m * k)) 的所有状态,我们会在 O(m) 时间内进行更新,同时对第二种类型的 new_col 和第一种类型的 O(1) 进行迭代。

我们可以做得比这更好。让我们为固定的il 找到最小的dp[i][j][l],遍历所有可能的j。假设它在列号min_j,值为x。对于所有j != min_j x 将用于更新i + 1 行中的第二个类型。

使用不同值更新的唯一列是min_j 本身,因为如果我们从
dp[i][min_j][l] 转到dp[i + 1][min_j][l + 1],我们不会切换该列。让我们像以前一样通过遍历所有j != min_j 来计算
dp[i + 1][min_j][l + 1]。操作总数为O(m)

复杂性将是 O(n * m * k),因为我们正在为所有不同的对 i, l 进行 O(m) 更新,其中有 O(n * k)

【讨论】:

  • 这确实是一个漂亮的解决方案,尤其是优化部分。优化部分Suppose it's in row number min_j, and the value is x这句话,你的意思是列而不是行号吧?
  • @torrtuga 谢谢!这正是我的意思。
  • k=0 时不会失败,因为它将采用第一行最小值,并且不切换列只会继续向下计算。
  • @torrtuga 我们不会改变我们进行第一类更新的方式,因为无论如何它们都是在O(1) 中完成的。在k = 0 的情况下,我们的答案将仅包含第一种类型的更新,例如我们不允许切换列。在dp[n - 1][j][0] 中将是j 第行中的数字总和。
【解决方案2】:

这可以使用自上而下的方法来解决。

您从第一行的某个位置开始,然后从那里向下遍历。你有两个选择:

a) 在同一列上继续 - 但您需要考虑到您有足够的行用于 K 开关。所以你的条件应该是remaining_rows >= K 然后你可以选择这个选项,否则不行。

b) 将您的列切换到任何其他列。考虑所有列。

在这两个选项中取最小值。

那么递归方程将是:

getMin(r, c, K) = min( 
                   (R - (r+1) >= K ? getMin(r+1, c, K) + A[r][c] : A[r][c] ),
                   ( for all j != c, j < C: min(getMin(r+1, c, K-1)) )
                  )

其中A 是输入矩阵,R 是行数,CA 中的列数。

并且你对所有c = 0C 取最小的getMin(0, c, K )

您可以记住每个r, c, K的解决方案。

对于 K = 0 ,只需向下遍历列并得到总和。

Java 中的代码是:

class Main {
    private static int[][][] dp;

    public static void main(String[] args) {

        int[][] A = { { 0, 1, 2 }, { 4, 3, 1 }, { 6, -2, 5 }, { 1, 4, -3 } };

        int K = 3;
        dp = new int[A.length][A[0].length][K + 1];
        for (int i = 0; i < A.length; i++) {
            for (int j = 0; j < A[0].length; j++) {
                for (int k = 1; k <= K; k++) {
                    dp[i][j][k] = Integer.MAX_VALUE;
                }
            }
        }
        int res = Integer.MAX_VALUE;
        for (int j = 0; j < A[0].length; j++)
            res = Math.min(res, getMin(A, 0, j, K));
        System.out.println(res);
    }

    private static int getMin(int[][] A, int r, int c, int K) {
        int R = A.length, C = A[0].length;
        if (r > R - 1 || c > C - 1)
            return Integer.MAX_VALUE;
        if (K == 0) {
            int sum = 0;
            for (int i = r; i < R; i++)
                sum += A[i][c];
            return sum;
        }
        if (dp[r][c][K] != Integer.MAX_VALUE)
            return dp[r][c][K];
        int min = Integer.MAX_VALUE;
        int rem_rows = R - (r + 1);
        for (int j = 0; j < C; j++) {
            if (j == c && rem_rows >= K) {
                int s = getMin(A, r + 1, j, K);
                if (s != Integer.MAX_VALUE) {
                    min = Math.min(min, s + A[r][c]);
                } else {
                    min = Math.min(min, A[r][c]);
                }

            } else if (j != c) {
                int m = getMin(A, r + 1, j, K - 1);
                if (m != Integer.MAX_VALUE) {
                    min = Math.min(min, m + A[r][c]);
                } else {
                    min = Math.min(min, A[r][c]);
                }
            }
        }
        dp[r][c][K] = min;
        return min;
    }
}

你可以玩live code here

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-12-05
    • 2016-01-14
    • 2021-12-20
    • 1970-01-01
    • 2020-09-12
    • 2013-06-18
    • 2013-11-26
    • 2016-07-20
    相关资源
    最近更新 更多