用二维前缀和快速计算每个裁剪邻域的总和,再比较均值阈值。
OJ: shumeng
题目 ID: CSP202104B
难度:普及-
标签:前缀和二维前缀和模拟
日期: 2026-07-31 16:21
形式化题目
给出一幅
思路
朴素做法是对每个像素遍历整个邻域求和,复杂度为
二维前缀和
判断较暗区域
- 对像素
,把邻域裁剪成矩形 ; - 用前缀和
求矩形内的总和与格子个数; - 用
比较,避免浮点误差。
先看一个直接枚举邻域格子的朴素解:
cpp
/**
* Author by Rainboy blog: https://rainboylv.com github: https://github.com/rainboylvx
* rbook: -> https://rbook.roj.ac.cn https://rbook2.roj.ac.cn
* rainboy的学习导航网站: https://idx.roj.ac.cn
* create_at: 2026-07-31 16:21
* update_at: 2026-08-17 22:39
*/
// brute.cpp:小数据暴力解,枚举每个像素邻域中的全部格子,直接求和再比较均值。
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 605;
int n, levels, radius, threshold;
int image[MAXN][MAXN]; // 原始灰度矩阵
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
cin >> n >> levels >> radius >> threshold;
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++) cin >> image[i][j];
}
int answer = 0;
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++) {
// 对每个像素,枚举其邻域内的所有格子求和
int total = 0, count = 0;
for (int x = max(1, i - radius); x <= min(n, i + radius); x++) {
for (int y = max(1, j - radius); y <= min(n, j + radius); y++) {
total += image[x][y];
count++;
}
}
if (total <= threshold * count) answer++;
}
}
cout << answer << '\n';
return 0;
}代码
cpp
/**
* Author by Rainboy blog: https://rainboylv.com github: https://github.com/rainboylvx
* rbook: -> https://rbook.roj.ac.cn https://rbook2.roj.ac.cn
* rainboy的学习导航网站: https://idx.roj.ac.cn
* create_at: 2026-07-31 16:21
* update_at: 2026-08-17 22:39
*/
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 605;
int n, levels, radius, threshold;
int sum[MAXN][MAXN]; // sum[i][j] 为以 (1,1) 到 (i,j) 为对角矩形的像素总和
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
cin >> n >> levels >> radius >> threshold;
// 边读入边构造二维前缀和
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++) {
int value;
cin >> value;
sum[i][j] = sum[i - 1][j] + sum[i][j - 1] - sum[i - 1][j - 1] + value;
}
}
int answer = 0;
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++) {
// 邻域在图像边界处裁剪为矩形
int top = max(1, i - radius), bottom = min(n, i + radius);
int left = max(1, j - radius), right = min(n, j + radius);
int total = sum[bottom][right] - sum[top - 1][right] - sum[bottom][left - 1] + sum[top - 1][left - 1];
int count = (bottom - top + 1) * (right - left + 1);
// 用乘法比较避免浮点误差:均值 <= t 等价于 总和 <= t * 个数
if (total <= threshold * count) answer++;
}
}
cout << answer << '\n';
return 0;
}复杂度
建表和枚举像素均为
总结
二维前缀和把每个邻域求和从遍历正方形降为四次数组访问;边界只需裁剪矩形端点。
