目录
🌟 构造矩阵:根据行列和还原二维矩阵【Leetcode 1605】
🌟 构造矩阵:根据行列和还原二维矩阵【Leetcode 1605】
📌 题目描述
你有两个非负整数数组 rowSum
和 colSum
,分别表示一个二维矩阵中每一行与每一列的元素之和。你并不知道这个矩阵的每个具体元素,但你知道:
rowSum[i]
是第i
行所有元素的和;colSum[j]
是第j
列所有元素的和。
现在请你构造一个 非负整数矩阵,其大小为 rowSum.length × colSum.length
,使得:
- 每行的元素和为
rowSum[i]
; - 每列的元素和为
colSum[j]
。
你只需要返回任意一个满足条件的矩阵即可。题目保证至少存在一个解。
🧠 解题分析
这个问题看似要求很多,但其本质是一个 资源分配问题 —— 把行和列的“容量”合理分配,填入矩阵中,使得每一行和每一列的和都刚好满足要求。
我们希望构建一个 m × n
的二维矩阵 res
,满足:
- 对任意
i
:sum(res[i][j] for j in range(n)) == rowSum[i]
- 对任意
j
:sum(res[i][j] for i in range(m)) == colSum[j]
这个问题可以通过贪心策略优雅地解决。因为每一步我们只需要满足局部最小限制即可,从而避免破坏全局约束。
🧩 解题方法:贪心算法
🎯 核心思想
我们从左上角开始,一步一步往右、往下填数。对于矩阵的每一个元素 (i, j)
,我们都填入当前这一行和这一列还剩多少的最小值:
val = min(rowSum[i], colSum[j])
这个 val
就是我们在 (i, j)
位置填入的值。填完后,我们更新 rowSum[i]
和 colSum[j]
:
rowSum[i] -= val
colSum[j] -= val
这样既不会超过当前行/列的限制,又尽量地“用完资源”。
✅ Python 实现
from typing import List
class Solution:
def restoreMatrix(self, rowSum: List[int], colSum: List[int]) -> List[List[int]]:
m, n = len(rowSum), len(colSum)
res = [[0] * n for _ in range(m)]
for i in range(m):
for j in range(n):
val = min(rowSum[i], colSum[j])
res[i][j] = val
rowSum[i] -= val
colSum[j] -= val
return res
🔍 示例说明
示例 1:
rowSum = [3, 8]
colSum = [4, 7]
可能的一个合法输出为:
[
[3, 0],
[1, 7]
]
验证:
- 第 0 行之和:3+0 = 3,满足;
- 第 1 行之和:1+7 = 8,满足;
- 第 0 列之和:3+1 = 4,满足;
- 第 1 列之和:0+7 = 7,满足。
⏱️ 时间与空间复杂度分析
- 时间复杂度:O(m × n),我们访问每一个矩阵元素一次。
- 空间复杂度:O(m × n),用于保存最终结果的二维矩阵。
其中,m
是 rowSum
的长度,n
是 colSum
的长度。
🔁 方法对比与拓展
1. 贪心 vs 网络流
这个问题在数学上属于**运输问题(Transportation Problem)**的一种特殊形式。它也可以用 最小费用最大流 来解决,尤其在更复杂限制下(如限制某个位置最大填多少)更适用。
但本题由于保证“必有解”,且无额外约束,贪心算法是最优选择,写法简单、效率高。
2. 多解情况
注意,题目要求“返回任意一个解”,因此合法解可能不止一个。例如:
rowSum = [5, 7, 10]
colSum = [8, 6, 8]
可能的多个合法输出之一为:
[
[5, 0, 0],
[3, 4, 0],
[0, 2, 8]
]
也可能得到:
[
[2, 3, 0],
[6, 0, 1],
[0, 3, 7]
]
只要满足每行、每列的和就可以。
📘 总结
这道题是一个典型的“构造类问题”,尤其适合作为 贪心算法应用的练习题。它考查你是否能把一个“全局约束”问题转化成局部最优策略,并逐步逼近整体最优。
- ✅ 关键:每次填最小值;
- ✅ 保证局部合法,即可保证全局合法;
- ✅ 最终保证 rowSum 和 colSum 都被“削减为0”,问题解决。