手把手教你用STM32和MATLAB搞定50Hz工频干扰:一个IIR陷波器的完整实现
2026/4/19 6:24:25
这是一块方格王国
每个格子:
1= 🌱 草地(可以建房)
0= 🌋 火山(不能建)
我们的任务是:
🏡 找一块全是草地的最大长方形区域,
给国王建一个大房子!
一个4 × 5 的地图:
行\列 1 2 3 4 5 1 1 1 0 1 1 2 1 1 0 1 1 3 1 1 1 1 1 4 0 1 1 1 0🤷♂️不知道哪里最大,那就全部试一遍!
这就是我们首先想的方法:
左上角:(x1, y1) 右下角:(x2, y2)换句话说,如果两个矩形,只要左上角与右上角有一个不相同,就是不同的矩形
1️⃣ 选左上角
2️⃣ 选右下角
3️⃣ 根据左上角,与右上角,枚举这个矩形内部的所有点,看看有不是全部都是1
4️⃣ 如果全部都是1,就与前面枚举的矩形面积比大小,更新max
👉 这就是使用最朴素枚举方法来解决这个问题!
#include <iostream> using namespace std; int a[25][25]; int n, m; int main() { cin >> n >> m; // 读入矩阵 for (int i = 1; i <= n; i++) { for (int j = 1; j <= m; j++) { cin >> a[i][j]; } } int ans = 0; // ① 枚举左上角行 for (int x1 = 1; x1 <= n; x1++) { // ② 枚举左上角列 for (int y1 = 1; y1 <= m; y1++) { // ③ 枚举右下角行 for (int x2 = x1; x2 <= n; x2++) { // ④ 枚举右下角列 for (int y2 = y1; y2 <= m; y2++) { bool ok = true; // ⑤ 枚举矩形内部行 for (int i = x1; i <= x2; i++) { // ⑥ 枚举矩形内部列 for (int j = y1; j <= y2; j++) { if (a[i][j] == 0) { ok = false; } } } // 如果这个矩形全是 1 if (ok) { int area = (x2 - x1 + 1) * (y2 - y1 + 1); ans = max(ans, area); } } } } } cout << ans << endl; return 0; }n,m < 12,是可以通过的n × m| 循环 | 次数 |
|---|---|
| 左上角 | n × m |
| 右下角 | n × m |
| 检查内部 | n × m |
O(n² · m² · n · m) = O(n³ · m³)二维前缀和不能减少“枚举矩形的次数”,
但可以把“检查一个矩形是否合法”的复杂度
从 O(面积) 降到 O(1)
每次:再扫一遍矩形内部,看是不是全 1
👉慢在“反复扫描矩形内部”
1️⃣预处理一个前缀和数组sum
2️⃣ 枚举所有矩形
3️⃣O(1) 判断矩形里 1 的个数
#include <iostream> using namespace std; int a[20][20]; int sum[20][20]; int main() { int n, m; cin >> n >> m; // 读入矩阵 for (int i = 1; i <= n; i++) { for (int j = 1; j <= m; j++) { cin >> a[i][j]; } } // 1️⃣ 计算二维前缀和 for (int i = 1; i <= n; i++) { for (int j = 1; j <= m; j++) { sum[i][j] = sum[i-1][j] + sum[i][j-1] - sum[i-1][j-1] + a[i][j]; } } int ans = 0; // 2️⃣ 枚举所有矩形 for (int x1 = 1; x1 <= n; x1++) { for (int x2 = x1; x2 <= n; x2++) { for (int y1 = 1; y1 <= m; y1++) { for (int y2 = y1; y2 <= m; y2++) { // 用前缀和 O(1) 计算矩形内 1 的个数 int ones = sum[x2][y2] - sum[x1-1][y2] - sum[x2][y1-1] + sum[x1-1][y1-1]; int area = (x2 - x1 + 1) * (y2 - y1 + 1); // 如果全是 1,更新答案 if (ones == area) { ans = max(ans, area); } } } } } cout << ans << endl; return 0; }| 部分 | 复杂度 |
|---|---|
| 前缀和预处理 | O(nm) |
| 枚举矩形 | O(n²m²) |
| 矩形合法性判断 | O(1) |
固定矩形的“上边”和“下边”,
把二维矩阵压缩成一维数组,
再在一维数组中找“最长连续 1 段”。
上边 + 下边 + 左边 + 右边👉 你是在枚举“矩形本身”
上边 + 下边 + 一次横向扫描👉 是在枚举“高度”,横向找宽度
❌ 枚举矩形
✅ 枚举“高度 + 连续宽度”
for (int top = 1; top <= n; top++) { for (int bottom = top; bottom <= n; bottom++) { ... } }👉 确定了矩形的高度
height = bottom - top + 1;对某一列j:
如果top ~ bottom这一列全是 1
👉 这一列是1
只要有一个0
👉 这一列是0
这样就得到一个一维 0/1 数组
在 0/1 数组中,找最长连续的 1
我们已经非常熟悉了:
if (col[j] == 1) cnt++; else cnt = 0;4️⃣ 面积怎么计算?
面积 = 连续列数 * 高度#include <iostream> using namespace std; int a[20][20]; int main() { int n, m; cin >> n >> m; // 读入矩阵 for (int i = 1; i <= n; i++) { for (int j = 1; j <= m; j++) { cin >> a[i][j]; } } int ans = 0; // 1️⃣ 固定上边 for (int top = 1; top <= n; top++) { // col[j] 表示:当前 top 到 bottom 之间, // 第 j 列是否全是 1 int col[20] = {0}; // 2️⃣ 枚举下边 for (int bottom = top; bottom <= n; bottom++) { // 更新每一列是否仍然“可用” for (int j = 1; j <= m; j++) { if (bottom == top) { // 第一行,直接赋值 col[j] = a[bottom][j]; } else { // 只要出现 0,这一列就废了 col[j] = col[j] && a[bottom][j]; } } // 当前矩形高度 int height = bottom - top + 1; // 3️⃣ 在 col[] 中找最长连续 1 int cnt = 0; for (int j = 1; j <= m; j++) { if (col[j] == 1) { cnt++; ans = max(ans, cnt * height); } else { cnt = 0; } } } } cout << ans << endl; return 0; }| 部分 | 复杂度 |
|---|---|
| 枚举 top, bottom | O(n²) |
| 每次扫描列 | O(m) |
👉总复杂度:O(n² · m)
👉 比前缀和的O(n² · m²)明显更快