【一百零二】【算法分析与设计】304. 二维区域和检索 - 矩阵不可变,1139. 最大的以 1 为边界的正方形,地毯,二维前缀和求区间累加和,二维差分处理重复多个数据

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

  • 最多调用 104sumRegion 方法

在这里插入图片描述

![外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

// #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)

给你一个由若干 01 组成的二维网格 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]01

在这里插入图片描述

在这里插入图片描述

#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 n50 m ≤ 100 m\le 100 m100

对于 100 % 100\% 100% 的数据,有 n , m ≤ 1000 n,m\le 1000 n,m1000

在这里插入图片描述

在这里插入图片描述

先求列前缀和再求行前缀和

#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(); // 计算并输出结果
}

结尾

最后,感谢您阅读我的文章,希望这些内容能够对您有所启发和帮助。如果您有任何问题或想要分享您的观点,请随时在评论区留言。

同时,不要忘记订阅我的博客以获取更多有趣的内容。在未来的文章中,我将继续探讨这个话题的不同方面,为您呈现更多深度和见解。

谢谢您的支持,期待与您在下一篇文章中再次相遇!

  • 15
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

妖精七七_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值