1605.给定行和列的和求可行矩阵

前言

题目来自LeetCode:https://leetcode.cn/problems/find-valid-matrix-given-row-and-column-sums/

题目(难度:中等)

给你两个非负整数数组 rowSum 和 colSum ,其中 rowSum[i] 是二维矩阵中第 i 行元素的和, colSum[j] 是第 j 列元素的和。换言之你不知道矩阵里的每个元素,但是你知道每一行和每一列的和。
请找到大小为 rowSum.length x colSum.length 的任意 非负整数 矩阵,且该矩阵满足 rowSum 和 colSum 的要求。
请你返回任意一个满足题目要求的二维矩阵,题目保证存在 至少一个 可行矩阵。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
示例 1:
输入:rowSum = [3,8], colSum = [4,7]
输出:[[3,0],
[1,7]]
解释:
第 0 行:3 + 0 = 3 == rowSum[0]
第 1 行:1 + 7 = 8 == rowSum[1]
第 0 列:3 + 1 = 4 == colSum[0]
第 1 列:0 + 7 = 7 == colSum[1]
行和列的和都满足题目要求,且所有矩阵元素都是非负的。
另一个可行的矩阵为:[[1,2],
[3,5]]
提示:
1 <= rowSum.length, colSum.length <= 500
0 <= rowSum[i], colSum[i] <= 10e8
sum(rowSum) == sum(colSum)

题解

本题难处在于暴力解决的时间复杂度是毁灭性的,最坏情况矩阵是每个元素都有10^8的可能,矩阵最大可达500*500=2.5×10^5,最坏情况可达2.5×10^13。
从优化暴力方法的角度入手,矩阵的每个元素res[i][j]都会小于第i行的和,第j列的和,也就是说,res[i][j] <= min(rowSum[i],colSum[j]),这样搜索范围就减小了,但这远远不够。
由于矩阵是可以取0的,不妨先让res[i][j]取min(rowSum[i],colSum[j]),每取完一个数,就在当前行和与当前列和上减去这个数,就算用尽当行(列)的和,当行(列)剩下的元素还能用0填补。

  1. 当且只有一行的时候,第一行的和永远会大于等于各列和,所以每次取的数都会是该列的和,数组rolSum即为答案。
  2. 当行数大于等于2时,矩阵n×m(n >= 2, m >= 1)的第一行按照上述规则取,整行取完后,第一行的行和也就是rowSum[0]会变成0,而colSum整个数组的和会减少rowSum[0],因为不止一行,所以此时colSum这个数组的和还是大于等于0的,又因为每次取的是min(rowSum[i],colSum[j]),所以colSum数组里每个数都还是大于等于0的。即取完一行后仍然和之前的性质没有发生变化。从而使所需矩阵减少成(n - 1) × m,循环往复,最终会使得n = 1,从而求出所需矩阵。

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Solution {
public int[][] restoreMatrix(int[] rowSum, int[] colSum) {
int row = rowSum.length;
int col = colSum.length;
int[][] res = new int[row][col];

for(int i = 0; i < row; i++){
for(int j = 0; j < col; j++){
//取当前行和与列和的较小值
res[i][j] = rowSum[i] < colSum[j] ? rowSum[i] : colSum[j];
//取完在当前行和与列和中减去
rowSum[i] -= res[i][j];
colSum[j] -= res[i][j];
}
}

return res;
}
}