304. 二维区域和检索 - 矩阵不可变 - 力扣(LeetCode)
给定一个二维矩阵
matrix
,以下类型的多个请求:
- 计算其子矩形范围内元素的总和,该子矩阵的 左上角 为
(row1, col1)
,右下角 为(row2, col2)
。实现
NumMatrix
类:
NumMatrix(int[][] matrix)
给定整数矩阵matrix
进行初始化
int sumRegion(int row1, int col1, int row2, int col2)
返回 左上角(row1, col1)
、右下角(row2, col2)
所描述的子矩阵的元素 总和 。示例 1:
输入: ["NumMatrix","sumRegion","sumRegion","sumRegion"] [[[[3,0,1,4,2],[5,6,3,2,1],[1,2,0,1,5],[4,1,0,1,7],[1,0,3,0,5]]],[2,1,4,3],[1,1,2,2],[1,2,2,4]] 输出: [null, 8, 11, 12] 解释: NumMatrix numMatrix = new NumMatrix([[3,0,1,4,2],[5,6,3,2,1],[1,2,0,1,5],[4,1,0,1,7],[1,0,3,0,5]]); numMatrix.sumRegion(2, 1, 4, 3); // return 8 (红色矩形框的元素总和) numMatrix.sumRegion(1, 1, 2, 2); // return 11 (绿色矩形框的元素总和) numMatrix.sumRegion(1, 2, 2, 4); // return 12 (蓝色矩形框的元素总和)
提示:
m == matrix.length
n == matrix[i].length
1 <= m, n <= 200
-105 <= matrix[i][j] <= 105
0 <= row1 <= row2 < m
0 <= col1 <= col2 < n
最多调用
104
次sumRegion
方法
// #define DEBUG
#ifdef DEBUG
#define bug(code) \
do { \
code \
} while (0)
#else
#define bug(code) \
do { \
} while (0)
#endif
class NumMatrix {
public:
int row, col; // 行和列的数量
vector<vector<int>> matrix; // 用于存储矩阵的前缀和
NumMatrix(vector<vector<int>>& _matrix) {
row = _matrix.size(); // 获取行数
col = _matrix[0].size(); // 获取列数
matrix = _matrix; // 初始化矩阵
for (int i = 0; i < row; i++) {
for (int j = 0; j < col; j++) {
// 计算前缀和
matrix[i][j] =
(i - 1 >= 0 ? matrix[i - 1][j] : 0) + // 加上上方元素的前缀和
(j - 1 >= 0 ? matrix[i][j - 1] : 0) - // 加上左侧元素的前缀和
((i - 1 >= 0 && j - 1 >= 0) ? matrix[i - 1][j - 1] : 0) + // 减去重复计算的左上角元素的前缀和
matrix[i][j]; // 加上当前元素
bug(cout << matrix[i][j] << " ";); // 输出调试信息
}
bug(cout << endl;); // 换行调试信息
}
}
int sumRegion(int row1, int col1, int row2, int col2) {
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0); // 提高输入输出效率
return matrix[row2][col2] - // 右下角元素的前缀和
(row1 - 1 >= 0 ? matrix[row1 - 1][col2] : 0) - // 减去上方多余部分的前缀和
(col1 - 1 >= 0 ? matrix[row2][col1 - 1] : 0) + // 减去左侧多余部分的前缀和
((row1 - 1 >= 0 && col1 - 1 >= 0) ? matrix[row1 - 1][col1 - 1] : 0); // 加回被重复减去的左上角元素的前缀和
}
};
/**
* Your NumMatrix object will be instantiated and called as such:
* NumMatrix* obj = new NumMatrix(matrix);
* int param_1 = obj->sumRegion(row1,col1,row2,col2);
*/
1139. 最大的以 1 为边界的正方形 - 力扣(LeetCode)
给你一个由若干
0
和1
组成的二维网格grid
,请你找出边界全部由1
组成的最大 正方形 子网格,并返回该子网格中的元素数量。如果不存在,则返回0
。示例 1:
输入:grid = [[1,1,1],[1,0,1],[1,1,1]] 输出:9
示例 2:
输入:grid = [[1,1,0,0]] 输出:1
提示:
1 <= grid.length <= 100
1 <= grid[0].length <= 100
grid[i][j]
为0
或1
#define ltu(i, a, b) for (int i = a; i <= b; i++) // 定义从a到b的循环
#define utl(i, a, b) for (int i = a; i >= b; i--) // 定义从a到b的反向循环
class Solution {
public:
vector<vector<int>> g; // 定义二维矩阵g
int ret; // 用于存储最大正方形边长的平方
int row, col; // 行数和列数
int nums = 1; // 用于存储当前计算的正方形边长
// 计算从(row1, col1)到(row2, col2)的子矩阵的元素总和
int gett(int row1, int col1, int row2, int col2) {
return g[row2][col2] - (col1 - 1 >= 0 ? g[row2][col1 - 1] : 0) -
(row1 - 1 >= 0 ? g[row1 - 1][col2] : 0) +
(row1 - 1 >= 0 && col1 - 1 >= 0 ? g[row1 - 1][col1 - 1] : 0);
}
void solve() {
ret = 0; // 初始化最大正方形的边长平方为0
row = g.size(), col = g[0].size(); // 获取行数和列数
nums = 1; // 初始化nums为1
ltu(i, 0, row - 1) { // 遍历每一行
ltu(j, 0, col - 1) { // 遍历每一列
// 计算前缀和
g[i][j] = (i - 1 >= 0 ? g[i - 1][j] : 0) +
(j - 1 >= 0 ? g[i][j - 1] : 0) -
(i - 1 >= 0 && j - 1 >= 0 ? g[i - 1][j - 1] : 0) +
g[i][j];
}
}
ltu(i, 0, row - 1) { // 遍历每一行
ltu(j, 0, col - 1) { // 遍历每一列
ltu(k, nums, min(row, col)) { // 遍历正方形的边长
int row1, col1, row2, col2;
row1 = i, col1 = j;
row2 = i + k - 1, col2 = j + k - 1;
if (row2 >= row || col2 >= col) // 检查是否越界
break;
if (row1 == row2 && col1 == col2) { // 检查1x1的正方形
if (gett(row1, col1, row2, col2) == 1) {
ret = max(ret, 1); // 更新最大正方形的边长平方
}
continue;
}
// 检查是否满足边界全部由1组成
if (4 * (k - 1) ==
gett(row1, col1, row2, col2) -
gett(row1 + 1, col1 + 1, row2 - 1, col2 - 1)) {
if ((k) * (k) > ret) { // 更新最大正方形的边长平方
ret = (k) * (k);
nums++;
}
}
}
}
}
}
int largest1BorderedSquare(vector<vector<int>>& _grid) {
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0); // 提高输入输出效率
g = _grid; // 初始化矩阵g
solve(); // 调用solve函数计算最大正方形的边长平方
return ret; // 返回结果
}
};
地毯 - 洛谷
地毯
题目描述
在 n × n n\times n n×n 的格子上有 m m m 个地毯。
给出这些地毯的信息,问每个点被多少个地毯覆盖。
输入格式
第一行,两个正整数 n , m n,m n,m。意义如题所述。
接下来 m m m 行,每行两个坐标 ( x 1 , y 1 ) (x_1,y_1) (x1,y1) 和 ( x 2 , y 2 ) (x_2,y_2) (x2,y2),代表一块地毯,左上角是 ( x 1 , y 1 ) (x_1,y_1) (x1,y1),右下角是 ( x 2 , y 2 ) (x_2,y_2) (x2,y2)。
输出格式
输出 n n n 行,每行 n n n 个正整数。
第 i i i 行第 j j j 列的正整数表示 ( i , j ) (i,j) (i,j) 这个格子被多少个地毯覆盖。
样例 #1
样例输入 #1
5 3
2 2 3 3
3 3 5 5
1 2 1 4
样例输出 #1
0 1 1 1 0
0 1 1 0 0
0 1 2 1 1
0 0 1 1 1
0 0 1 1 1
提示
样例解释
覆盖第一个地毯后:
0 0 0 | 0 0 0 | 0 0 0 | 0 0 0 | 0 0 0 |
---|---|---|---|---|
0 0 0 | 1 1 1 | 1 1 1 | 0 0 0 | 0 0 0 |
0 0 0 | 1 1 1 | 1 1 1 | 0 0 0 | 0 0 0 |
0 0 0 | 0 0 0 | 0 0 0 | 0 0 0 | 0 0 0 |
0 0 0 | 0 0 0 | 0 0 0 | 0 0 0 | 0 0 0 |
覆盖第一、二个地毯后:
0 0 0 | 0 0 0 | 0 0 0 | 0 0 0 | 0 0 0 |
---|---|---|---|---|
0 0 0 | 1 1 1 | 1 1 1 | 0 0 0 | 0 0 0 |
0 0 0 | 1 1 1 | 2 2 2 | 1 1 1 | 1 1 1 |
0 0 0 | 0 0 0 | 1 1 1 | 1 1 1 | 1 1 1 |
0 0 0 | 0 0 0 | 1 1 1 | 1 1 1 | 1 1 1 |
覆盖所有地毯后:
0 0 0 | 1 1 1 | 1 1 1 | 1 1 1 | 0 0 0 |
---|---|---|---|---|
0 0 0 | 1 1 1 | 1 1 1 | 0 0 0 | 0 0 0 |
0 0 0 | 1 1 1 | 2 2 2 | 1 1 1 | 1 1 1 |
0 0 0 | 0 0 0 | 1 1 1 | 1 1 1 | 1 1 1 |
0 0 0 | 0 0 0 | 1 1 1 | 1 1 1 | 1 1 1 |
数据范围
对于 20 % 20\% 20% 的数据,有 n ≤ 50 n\le 50 n≤50, m ≤ 100 m\le 100 m≤100。
对于 100 % 100\% 100% 的数据,有 n , m ≤ 1000 n,m\le 1000 n,m≤1000。
先求列前缀和再求行前缀和
#include<bits/stdc++.h> // 引入所有标准库
using namespace std;
#define int long long // 定义int为long long
#define endl '\n' // 定义换行符
#define FAST() ios::sync_with_stdio(0),cin.tie(0),cout.tie(0) // 定义快速输入输出
#define p pair<int,int> // 定义p为pair<int,int>
#define ff first // 定义ff为first
#define ss second // 定义ss为second
#define ltu(i,a,b) for(int i=a;i<=b;i++) // 定义从a到b的循环
#define utl(i,a,b) for(int i=a;i>=b;i--) // 定义从a到b的反向循环
int n, m; // 定义全局变量n和m
struct node { // 定义结构体node
int row1, col1; // 左上角的行和列
int row2, col2; // 右下角的行和列
};
vector<node> readd; // 定义一个存储node的向量
vector<vector<int>> g; // 定义二维向量g,用于存储每个格子的覆盖次数
// 更新矩形区域
void sett(int row1, int col1, int row2, int col2) {
g[row1][col1] += 1; // 左上角+1
g[row1][col2 + 1] -= 1; // 右上角的右边一列-1
g[row2 + 1][col1] -= 1; // 左下角的下一行-1
g[row2 + 1][col2 + 1] += 1; // 右下角的下一行和右边一列+1
}
void solve() {
g.assign(n + 1 + 5, vector<int>(n + 1 + 5, 0)); // 初始化二维向量g,大小为(n+6)*(n+6),初始值为0
for (auto& x : readd) { // 遍历所有的地毯
sett(x.row1, x.col1, x.row2, x.col2); // 更新每个地毯覆盖的区域
}
// 计算每个格子的覆盖次数,按列累加
ltu(j, 1, n) {
ltu(i, 1, n) {
g[i][j] = g[i - 1][j] + g[i][j]; // 当前格子的值等于上面格子的值加上当前格子的值
}
}
// 计算每个格子的覆盖次数,按行累加
ltu(i, 1, n) {
ltu(j, 1, n) {
g[i][j] = g[i][j - 1] + g[i][j]; // 当前格子的值等于左边格子的值加上当前格子的值
cout << g[i][j] << " "; // 输出当前格子的覆盖次数
}
cout << endl; // 每行输出完后换行
}
}
signed main() {
FAST(); // 启用快速输入输出
cin >> n >> m; // 输入n和m
readd.clear(); // 清空readd向量
ltu(i, 1, m) { // 遍历所有的地毯
node temp; // 定义一个临时变量temp
cin >> temp.row1 >> temp.col1 >> temp.row2 >> temp.col2; // 输入地毯的左上角和右下角坐标
readd.push_back(temp); // 将temp加入到readd向量中
}
solve(); // 计算并输出结果
}
列前缀和和行前缀和一起求
#include<bits/stdc++.h> // 引入所有标准库
using namespace std;
#define int long long // 定义int为long long
#define endl '\n' // 定义换行符
#define FAST() ios::sync_with_stdio(0),cin.tie(0),cout.tie(0) // 定义快速输入输出
#define p pair<int,int> // 定义p为pair<int,int>
#define ff first // 定义ff为first
#define ss second // 定义ss为second
#define ltu(i,a,b) for(int i=a;i<=b;i++) // 定义从a到b的循环
#define utl(i,a,b) for(int i=a;i>=b;i--) // 定义从a到b的反向循环
int n, m; // 定义全局变量n和m
struct node { // 定义结构体node
int row1, col1; // 左上角的行和列
int row2, col2; // 右下角的行和列
};
vector<node> readd; // 定义一个存储node的向量
vector<vector<int>> g; // 定义二维向量g,用于存储每个格子的覆盖次数
// 更新矩形区域
void sett(int row1, int col1, int row2, int col2) {
g[row1][col1] += 1; // 左上角+1
g[row1][col2 + 1] -= 1; // 右上角的右边一列-1
g[row2 + 1][col1] -= 1; // 左下角的下一行-1
g[row2 + 1][col2 + 1] += 1; // 右下角的下一行和右边一列+1
}
void solve() {
g.assign(n + 1 + 5, vector<int>(n + 1 + 5, 0)); // 初始化二维向量g,大小为(n+6)*(n+6),初始值为0
for (auto& x : readd) { // 遍历所有的地毯
sett(x.row1, x.col1, x.row2, x.col2); // 更新每个地毯覆盖的区域
}
// 计算每个格子的覆盖次数
ltu(i, 1, n) { // 遍历每一行
ltu(j, 1, n) { // 遍历每一列
// 当前格子的覆盖次数等于左边格子和上面格子的覆盖次数之和,减去左上角格子的覆盖次数,加上当前格子的初始值
g[i][j] = g[i][j - 1] + g[i - 1][j] + g[i][j] - g[i - 1][j - 1];
cout << g[i][j] << " "; // 输出当前格子的覆盖次数
}
cout << endl; // 每行输出完后换行
}
}
signed main() {
FAST(); // 启用快速输入输出
cin >> n >> m; // 输入n和m
readd.clear(); // 清空readd向量
ltu(i, 1, m) { // 遍历所有的地毯
node temp; // 定义一个临时变量temp
cin >> temp.row1 >> temp.col1 >> temp.row2 >> temp.col2; // 输入地毯的左上角和右下角坐标
readd.push_back(temp); // 将temp加入到readd向量中
}
solve(); // 计算并输出结果
}
结尾
最后,感谢您阅读我的文章,希望这些内容能够对您有所启发和帮助。如果您有任何问题或想要分享您的观点,请随时在评论区留言。
同时,不要忘记订阅我的博客以获取更多有趣的内容。在未来的文章中,我将继续探讨这个话题的不同方面,为您呈现更多深度和见解。
谢谢您的支持,期待与您在下一篇文章中再次相遇!