- Leetcode 第 108 场双周赛 Problem D 黑格子的数目
- 题目
- 给你两个整数 m 和 n ,表示一个下标从 0 开始的 m x n 的网格图。
- 给你一个下标从 0 开始的二维整数矩阵 coordinates ,其中 coordinates[i] = [x, y] 表示坐标为 [x, y] 的格子是 黑色的 ,所有没出现在 coordinates 中的格子都是 白色的。
- 一个块定义为网格图中 2 x 2 的一个子矩阵。更正式的,对于左上角格子为 [x, y] 的块,其中 0 <= x < m - 1 且 0 <= y < n - 1 ,包含坐标为 [x, y] ,[x + 1, y] ,[x, y + 1] 和 [x + 1, y + 1] 的格子。
- 请你返回一个下标从 0 开始长度为 5 的整数数组 arr ,arr[i] 表示恰好包含 i 个 黑色 格子的块的数目。
- 2 <= m <= 105
- 2 <= n <= 105
- 0 <= coordinates.length <= 104
- coordinates[i].length == 2
- 0 <= coordinates[i][0] < m
- 0 <= coordinates[i][1] < n
- coordinates 中的坐标对两两互不相同。
- 解法
- m*n 比较大,因此不能遍历矩阵,但是黑色块比较少,因此可以由黑色块入手,计算 1、2、3、4 块黑格的个数,然后总个数减去所有就是 0 快黑格的个数,
- 遍历所有黑格,将其作为符合要求的黑格的 左上角、左下角、右上角、右下角,同时判断该 2*2 矩形其余位置黑格数,然后计算出 1、2、3、4 块黑格的矩阵数(含重复),
- 最后要注意,两个黑格子就会重复计算两次、三个重复计算三次、四个重复计算四次,因此除掉即可
- 总个数可以取每个矩阵的右下角,除第一行与第一列外均可做右下角,因此总个数为 (m-1)*(n-1)
- 为了方便可以使用 HashSet 存储格子下标,使用二维转一维存储与计算:m*maxN+n
- 时间复杂度:O(coordinates),空间复杂度:O(coordinates)
- 代码
private long[] solution(int m, int n, int[][] coordinates) {
long maxN = 100000;
long[] res = new long[5];
if (coordinates.length > 0) {
Set<Long> blockSet = saveBlock(coordinates, maxN);
doAllBlackBlocks(coordinates, blockSet, maxN, res, m, n);
res[2] /= 2;
res[3] /= 3;
res[4] /= 4;
}
long total = (long)(m - 1) * (n - 1);
res[0] = total - res[1] - res[2] - res[3] - res[4];
return res;
}
private void doAllBlackBlocks(int[][] coordinates, Set<Long> blockSet, long maxN, long[] res, int m, int n) {
for (int i = 0; i < coordinates.length; i++) {
int x = coordinates[i][0];
int y = coordinates[i][1];
int[][] leftUpper = new int[][]{{0,0},{0,1},{1,0},{1,1}};
int black = getBlackNum(blockSet, leftUpper, x, y, maxN, m, n);
if (black > 0) {
res[black]++;
}
int[][] leftLower = new int[][]{{-1,0},{-1,1},{0,0},{0,1}};
black = getBlackNum(blockSet, leftLower, x, y, maxN, m, n);
if (black > 0) {
res[black]++;
}
int[][] rightUpper = new int[][]{{0,-1},{0,0},{1,-1},{1,0}};
black = getBlackNum(blockSet, rightUpper, x, y, maxN, m, n);
if (black > 0) {
res[black]++;
}
int[][] rightLower = new int[][]{{-1,-1},{-1,0},{0,-1},{0,0}};
black = getBlackNum(blockSet, rightLower, x, y, maxN, m, n);
if (black > 0) {
res[black]++;
}
}
}
private int getBlackNum(Set<Long> blockSet, int[][] direction, int x, int y, long maxN, int m, int n) {
int black = 0;
int xTemp = x;
int yTemp = y;
for (int i = 0; i < direction.length; i++) {
xTemp = x + direction[i][0];
yTemp = y + direction[i][1];
if (xTemp >= 0 && xTemp < m && yTemp >= 0 && yTemp < n) {
if (blockSet.contains(toDimensional(xTemp, yTemp, maxN))) {
black++;
}
} else {
black = -1;
break;
}
}
return black;
}
private Set<Long> saveBlock(int[][] coordinates, long maxN) {
Set<Long> blockSet = new HashSet<>(coordinates.length << 2);
for (int i = 0; i < coordinates.length; i++) {
blockSet.add(toDimensional(coordinates[i][0], coordinates[i][1], maxN));
}
return blockSet;
}
private long toDimensional(int x, int y, long maxN) {
return x * maxN + y;
}