ccf-csp计算机软件能力认证202104-2邻域均值(C语言版)
题目内容:
试题背景
顿顿在学习了数字图像处理后,想要对手上的一副灰度图像进行降噪处理。不过该图像仅在较暗区域有很多噪点,如果贸然对全图进行降噪,会在抹去噪点的同时也模糊了原有图像。因此顿顿打算先使用邻域均值来判断一个像素是否处于较暗区域,然后仅对处于较暗区域的像素进行降噪处理。
问题描述
待处理的灰度图像长宽皆为n个像素,可以表示为一个n*n大小的矩阵A,其中每个元素是一个[0,L)范围内的整数,表示对应位置像素的灰度值。
对于矩阵中任意一个元素Aij(0≤i,j<n),其邻域定义为附近若干元素的集和:
这里使用了一个额外的参数r来指明Aij附近元素的具体范围。根据定义,易知Neighbor(i,j,r)最多有(2r+1)^2个元素。
如果元素Aij邻域中所有元素的平均值小于或等于一个给定的阈值t,我们就认为该元素对应位置的像素处于较暗区域。下图给出了两个例子,左侧图像的较暗区域在右侧图像中展示为黑色,其余区域展示为白色。
现给定邻域参数 r 和阈值 t,试统计输入灰度图像中有多少像素处于较暗区域。
输入格式
输入共 n+1 行。
输入的第一行包含四个用空格分隔的正整数 n、L、r 和 t,含义如前文所述。
第二到第 n+1 行输入矩阵 A。第 i+2(0≤i<n)行包含用空格分隔的 n 个整数,依次为 Ai0,Ai1,⋯,Ai(n−1)。
输出格式
输出一个整数,表示输入灰度图像中处于较暗区域的像素总数。
样例输入
4 16 1 6
0 1 2 3
4 5 6 7
8 9 10 11
12 13 14 15
样例输出
7
样例输入
11 8 2 2
0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0
0 7 0 0 0 7 0 0 7 7 0
7 0 7 0 7 0 7 0 7 0 7
7 0 0 0 7 0 0 0 7 0 7
7 0 0 0 0 7 0 0 7 7 0
7 0 0 0 0 0 7 0 7 0 0
7 0 7 0 7 0 7 0 7 0 0
0 7 0 0 0 7 0 0 7 0 0
0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0
样例输出
83
数据规模和约定
70% 的测试数据满足 n≤100、r≤10。
全部的测试数据满足 0<n≤600、0<r≤100 且 2≤t<L≤256。
解题思路
1.由于要进行统一操作,所以需要先将所有数据存入计算机,再进行处理,此处由于n最大为600,故使用二维数组存取。
2.首先想到的肯定是暴力破解,对每一个元素进行周围邻域的计算,对于中心元素,邻域左上元素为s[a][b],右下元素为s[c][d],对这个方块儿内的元素进行计算即可,但结果只有70分。分析一下:假设当n=100,r=10的时候,明明每一个元素的邻域都相同,但咱们却需要对每一个元素重新计算,显然会超出运算时间,并且进行了很多没有必要的计算,所以我们自然应该想到利用前一个元素的邻域来计算后一个元素的邻域。
3.所以对于每一行第一个元素,我们计算其邻域值,后续元素的邻域,我们根据左边界和右边界的变化,进行相应的减加操作即可,最终达到100分。
Ps:如果想要继续优化,可以在之前的基础上,对于每一列的第一个元素计算其邻域值,后续元素的邻域,根据上边界和下边界的变化,进行计算即可。但由于此题在上一步操作后,就已经将问题的复杂度降了一次,达到了要求,所以没有这个必要,也就不在这里写了。
提交后得100分的C语言程序如下:
#include<stdio.h>
int main(){
int i,j,n,L,r,t,t1,t2,end=0;
int a,b,c,d;//对于中心元素,邻域左上元素为s[a][b],右下元素为s[c][d]
int a1,b1,c1,d1;//对于新的中心元素,邻域左上元素为s[a1][b1],右下元素为s[c1][d1]
float sum,num;//sum为目前元素总值,num为目前元素总个数
scanf("%d%d%d%d",&n,&L,&r,&t);
int s[n][n];
for(i=0;i<n;i++) for(j=0;j<n;j++) scanf("%d",&s[i][j]);
for(i=0;i<n;i++){
j=0;sum=0;num=0;//每一行对第一个元素计算,重新赋初值
//确定邻域范围a,b,c,d
a=i-r;if(a<0) a=0;
b=j-r;if(b<0) b=0;
c=i+r;if(c>=n) c=n-1;
d=j+r;if(d>=n) d=n-1;
//计算第一个元素的邻域
for(t1=a;t1<c+1;t1++){
for(t2=b;t2<d+1;t2++){
sum+=s[t1][t2];
num++;
}
}
if(sum/num<=t) end++;
//计算本行之后元素的邻域
for(j=1;j<n;j++){
//确定新的邻域范围a1,b1,c1,d1
a1=i-r;if(a1<0) a1=0;
b1=j-r;if(b1<0) b1=0;
c1=i+r;if(c1>=n) c1=n-1;
d1=j+r;if(d1>=n) d1=n-1;
//判断左边界是否变化,若变化则减去
if(b1!=b){
for(t1=a;t1<c+1;t1++){
sum-=s[t1][b];
num--;
}
}
//判断右边界是否变化,若变化则加上
if(d1!=d){
for(t1=c;t1>=a;t1--){
sum+=s[t1][d1];
num++;
}
}
if(sum/num<=t) end++;
a=a1;b=b1;c=c1;d=d1;//重新定义目前邻域范围
}
}
printf("%d",end);
return 0;
}