水印检查
时间限制: 1.0 秒
空间限制: 512 MiB
相关文件: 题目目录
题目背景
一幅长宽分别为 个像素和
个像素的灰度图像可以表示为一个
大小的矩阵
。其中每个元素
(
、
)是一个
范围内的整数,表示对应位置像素的灰度值。具体来说,一个
比特的灰度图像中每个像素的灰度范围是
。
题目描述
小 P 在学习了数字图像处理后,设计了一种形如 CSP 的水印。具体来说,若想检测出水印,需选定一个阈值参数 ,然后将图像二值化:
灰度值大于等于
的像素变为白色;
灰度值小于
的像素变为黑色。
然后检查图像中是否有 的区域如下所示(为方便展示用绿色替代白色):

p02-01.png
即呈现出 CSP 三个字母的形状。
该水印较为简单,无需考虑旋转、翻转等复杂情况;只需检查灰度图像 是否包含一个大小为
的子矩阵
,其中大于等于阈值
和小于的像素分布与上图完全一致。
对于给定的 大小的灰度图像
,试计算出所有能检查出水印
CSP 的阈值 。
输入格式
从标准输入读入数据。
输入的第一行包含由空格分隔的两个正整数 和
,表示图像的大小和像素灰度值的范围。
接下来 行输入矩阵
,其中第
行(
)包含用空格分隔的
个整数,依次为
。
输出格式
输出到标准输出。
输出若干行,每行包含一个整数,表示一个可以检查出水印 CSP 的阈值 。
范围内所有可行阈值
按从小到大顺序输出。
样例输入
9 256
9 9 8 8 9 9 9 8 255
9 0 0 8 0 0 7 0 8
9 0 0 8 7 9 7 7 5
9 0 0 0 0 8 7 0 0
7 7 8 7 7 8 8 6 5
6 2 2 5 1 1 5 1 6
6 2 2 6 6 6 7 5 3
6 2 2 2 1 5 8 1 1
7 7 8 7 7 8 8 2 3
样例输出
4
5
7
样例解释
或
时图像如下所示,其中
* 和 - 分别表示白色和黑色,水印出现在后五行:
* * * * * * * * *
* - - * - - * - *
* - - * * * * * *
* - - - - * * - -
* * * * * * * * *
* - - * - - * - *
* - - * * * * * -
* - - - - * * - -
* * * * * * * - -
时图像如下所示,容易发现前五行(
)显现出水印
CSP:
* * * * * * * * *
* - - * - - * - *
* - - * * * * * -
* - - - - * * - -
* * * * * * * - -
- - - - - - - - -
- - - - - - * - -
- - - - - - * - -
* * * * * * * - -
子任务
的测试点满足:
、
;
全部的测试点满足:、
,像素值均在
范围内,且保证至少存在一个阈值可以检测出水印
CSP。
Solution
import java.io.InputStream;
import java.io.IOException;
public class Main {
// 1 表示白色,0 表示黑色
private static final int[][] MASK = {
{1, 1, 1, 1, 1, 1, 1, 1, 1},
{1, 0, 0, 1, 0, 0, 1, 0, 1},
{1, 0, 0, 1, 1, 1, 1, 1, 0},
{1, 0, 0, 0, 0, 1, 1, 0, 0},
{1, 1, 1, 1, 1, 1, 1, 0, 0}
};
public static void main(String[] args) throws Exception {
FastScanner scanner = new FastScanner();
int n = scanner.nextInt();
int L = scanner.nextInt();
int[][] image = new int[n][n];
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
image[i][j] = scanner.nextInt();
}
}
// 差分数组,阈值范围为 [0, L - 1]
int[] diff = new int[L + 1];
for (int top = 0; top + 4 < n; top++) {
for (int leftColumn = 0; leftColumn + 8 < n; leftColumn++) {
int lower = 0;
int upper = L - 1;
for (int i = 0; i < 5; i++) {
for (int j = 0; j < 9; j++) {
int value = image[top + i][leftColumn + j];
if (MASK[i][j] == 1) {
// 模板要求白色:value >= k
upper = Math.min(upper, value);
} else {
// 模板要求黑色:value < k
lower = Math.max(lower, value + 1);
}
}
}
if (lower <= upper) {
diff[lower]++;
diff[upper + 1]--;
}
}
}
int count = 0;
StringBuilder answer = new StringBuilder();
for (int k = 0; k < L; k++) {
count += diff[k];
if (count > 0) {
answer.append(k).append('\n');
}
}
System.out.print(answer);
}
private static class FastScanner {
private final InputStream input = System.in;
private final byte[] buffer = new byte[1 << 16];
private int pointer = 0;
private int length = 0;
private int read() throws IOException {
if (pointer >= length) {
length = input.read(buffer);
pointer = 0;
if (length == -1) {
return -1;
}
}
return buffer[pointer++];
}
int nextInt() throws IOException {
int c;
do {
c = read();
} while (c <= 32 && c != -1);
int sign = 1;
if (c == '-') {
sign = -1;
c = read();
}
int value = 0;
while (c > 32 && c != -1) {
value = value * 10 + (c - '0');
c = read();
}
return value * sign;
}
}
}