leetcode
leetcode 2051 ~ 2100
给定条件下构造矩阵

给定条件下构造矩阵

难度:

标签:

题目描述

给你一个  整数 k ,同时给你:

  • 一个大小为 n 的二维整数数组 rowConditions ,其中 rowConditions[i] = [abovei, belowi] 和
  • 一个大小为 m 的二维整数数组 colConditions ,其中 colConditions[i] = [lefti, righti] 。

两个数组里的整数都是 1 到 k 之间的数字。

你需要构造一个 k x k 的矩阵,1 到 k 每个数字需要 恰好出现一次 。剩余的数字都是 0 。

矩阵还需要满足以下条件:

  • 对于所有 0 到 n - 1 之间的下标 i ,数字 abovei 所在的  必须在数字 belowi 所在行的上面。
  • 对于所有 0 到 m - 1 之间的下标 i ,数字 lefti 所在的  必须在数字 righti 所在列的左边。

返回满足上述要求的 任意 矩阵。如果不存在答案,返回一个空的矩阵。

 

示例 1:

输入:k = 3, rowConditions = [[1,2],[3,2]], colConditions = [[2,1],[3,2]]
输出:[[3,0,0],[0,0,1],[0,2,0]]
解释:上图为一个符合所有条件的矩阵。
行要求如下:
- 数字 1 在第 1 行,数字 2 在第 2 行,1 在 2 的上面。
- 数字 3 在第 0 行,数字 2 在第 2 行,3 在 2 的上面。
列要求如下:
- 数字 2 在第 1 列,数字 1 在第 2 列,2 在 1 的左边。
- 数字 3 在第 0 列,数字 2 在第 1 列,3 在 2 的左边。
注意,可能有多种正确的答案。

示例 2:

输入:k = 3, rowConditions = [[1,2],[2,3],[3,1],[2,3]], colConditions = [[2,1]]
输出:[]
解释:由前两个条件可以得到 3 在 1 的下面,但第三个条件是 3 在 1 的上面。
没有符合条件的矩阵存在,所以我们返回空矩阵。

 

提示:

  • 2 <= k <= 400
  • 1 <= rowConditions.length, colConditions.length <= 104
  • rowConditions[i].length == colConditions[i].length == 2
  • 1 <= abovei, belowi, lefti, righti <= k
  • abovei != belowi
  • lefti != righti

代码结果

运行时间: 68 ms, 内存: 22.9 MB


/*
 * Problem: Construct a k x k matrix where each number from 1 to k appears exactly once
 * and the matrix satisfies row and column conditions given by rowConditions and colConditions.
 * Approach:
 * 1. Use topological sorting to determine the order of numbers in rows and columns.
 * 2. If there is a cycle detected during sorting, return an empty matrix.
 * 3. Place the numbers in the matrix according to the determined row and column orders.
 */

import java.util.*;
import java.util.stream.*;

public class MatrixConstructor {
    public int[][] buildMatrix(int k, int[][] rowConditions, int[][] colConditions) {
        int[][] result = new int[k][k];
        int[] rowOrder = topologicalSort(k, rowConditions);
        int[] colOrder = topologicalSort(k, colConditions);
        if (rowOrder == null || colOrder == null) return new int[0][0];

        Map<Integer, Integer> rowIndex = IntStream.range(0, k)
                .boxed()
                .collect(Collectors.toMap(i -> rowOrder[i], i -> i));
        Map<Integer, Integer> colIndex = IntStream.range(0, k)
                .boxed()
                .collect(Collectors.toMap(i -> colOrder[i], i -> i));

        IntStream.rangeClosed(1, k).forEach(i -> result[rowIndex.get(i)][colIndex.get(i)] = i);

        return result;
    }

    private int[] topologicalSort(int k, int[][] conditions) {
        int[] inDegree = new int[k + 1];
        List<Integer>[] graph = Stream.generate(ArrayList<Integer>::new)
                                      .limit(k + 1)
                                      .toArray(ArrayList[]::new);
        Arrays.stream(conditions).forEach(cond -> {
            graph[cond[0]].add(cond[1]);
            inDegree[cond[1]]++;
        });

        Queue<Integer> queue = IntStream.rangeClosed(1, k)
                                        .filter(i -> inDegree[i] == 0)
                                        .boxed()
                                        .collect(Collectors.toCollection(LinkedList::new));

        int[] order = new int[k];
        int index = 0;
        while (!queue.isEmpty()) {
            int node = queue.poll();
            order[index++] = node;
            graph[node].forEach(neighbor -> {
                if (--inDegree[neighbor] == 0) queue.offer(neighbor);
            });
        }

        return index == k ? order : null;
    }
}

解释

方法:

题解的核心思想是使用拓扑排序来确定行和列的相对顺序。首先,将 `rowConditions` 和 `colConditions` 分别视为有向图中的边,构建两个图:一个用于行的约束,另一个用于列的约束。然后对这两个图进行拓扑排序。如果图中存在环(即拓扑排序返回空),则说明没有合法的布局满足所有条件,返回空矩阵。如果排序成功,使用排序结果来构建矩阵,其中行排序结果决定数字在矩阵中的行,列排序结果决定数字在矩阵中的列。最后,根据这两个结果构建最终的矩阵。

时间复杂度:

O(k^2 + n + m)

空间复杂度:

O(k^2 + n + m)

代码细节讲解

🦆
如何确保构建的两个有向图完全表达了rowConditions和colConditions中的所有约束关系?
在构建有向图时,每个条件(例如从rowConditions和colConditions中的每一对[a, b])被转化为从节点a到节点b的一条有向边。这样,每条边恰好表示一个'前后'关系,确保了所有的约束关系都能在图中表达。初始化图结构时,为每个可能的顶点(这里是1到k)创建一个空列表,然后遍历条件列表,将每个条件添加到相应顶点(减1处理因为数组从0开始索引)的邻接表中。这样,构建的图将完全反映了输入的约束关系。
🦆
在拓扑排序中,如果存在环,解题思路中提到返回空矩阵。请问如何有效地检测这些图中是否存在环?
在拓扑排序过程中,我们通过计算每个节点的入度来检测环。初始化时,计算每个节点的入度。在排序过程中,选择入度为0的节点,将其从图中移除,并减少其相邻节点的入度。如果在某一时刻没有入度为0的节点,而图中还有未处理的节点,这表明存在环。另一种检测方法是,如果最终排序的结果数量小于节点总数,也表明图中存在环。
🦆
在拓扑排序后如何处理多个有效排序结果?解题思路是否考虑了不同拓扑排序结果对最终矩阵的影响?
拓扑排序可能有多个有效的结果,这取决于图中节点的相对独立性。如果存在多种有效的拓扑排序,每种排序都可以用来构建一个符合条件的矩阵。解题思路中并未特别处理多个拓扑排序结果;它只是利用了一种可能的排序结果来构建矩阵。在实际应用中,这意味着如果存在多种拓扑排序,可能会有多个符合条件的矩阵解。
🦆
题解中提到使用字典`mp`来映射列位置,这种映射方式是否可能导致某些数字在矩阵中的位置不符合初始的colConditions约束?
使用字典`mp`来映射列位置是基于列的拓扑排序,它应该完全遵循colConditions的约束。这种映射方法确保了每个数字在列的位置是按照列的拓扑排序确定的,因此不会违反初始的列约束。只要拓扑排序正确无误地反映了所有的列约束,使用这种映射方式构建矩阵就不会出现位置错误。

相关问题