CCF CSP 2021-04-2 邻域均值 题解及满分代码(C++11)

2023-05-16

文章目录

    • 题目描述
    • 问题分析
    • 70分解法
    • 满分解法

题目描述

在这里插入图片描述
在这里插入图片描述
现给定邻域参数 r 和阈值 t,试统计输入灰度图像中有多少像素处于较暗区域

输入格式

输入共 n+1 行。

输入的第一行包含四个用空格分隔的正整数 n、L、r 和 t,含义如前文所述。

第二到第 n+1 行输入矩阵 A。
第 i+2 ( 0 ≤ i < n ) (0≤i<n) 0i<n行包含用空格分隔的 n 个整数,依次为 A i 0 , A i 1 , ⋯ , A i ( n − 1 ) A_{i0},A_{i1},⋯,A_{i(n−1)} Ai0,Ai1,,Ai(n1)

输出格式

输出一个整数,表示输入灰度图像中处于较暗区域的像素总数。

样例输入

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 n≤100 n100 r ≤ 10 r≤10 r10

全部的测试数据满足 0 < n ≤ 600 0<n≤600 0<n600 0 < r ≤ 100 0<r≤100 0<r100 2 ≤ t < L ≤ 256 2≤t<L≤256 2t<L256

问题分析

题中要点共有三个:

  1. 邻域:对于矩阵内每一个点 A [ i ] [ j ] A[i][j] A[i][j]来说,其邻域内的点 A [ x ] [ y ] A[x][y] A[x][y]满足: 0 ≤ x , y < n 且 ∣ x − i ∣ ≤ r 且 ∣ y − j ∣ ≤ r {0≤x,y<n 且 |x−i|≤r 且 |y−j|≤r} 0x,y<nxiryjr,其中r为输入的一个正整数
  2. 邻域均值:对于矩阵内每一个点 A [ i ] [ j ] A[i][j] A[i][j],其邻域均值为其邻域内所有点的值的均值
  3. 阈值t

求:矩阵A中邻域均值小于等于t的所有点的个数

由此我们很容易就可以得到解题思路:==把所有点的邻域均值求出来,与t比较判断就得出了答案。==但能不能拿满分的关键在于求均值的过程应该如何实现,因为这种题如果用死方法去一次一次地遍历算是很容易运行超时的。但没有更好思路的时候,先从最基本的方法开始实现也许会在过程中产生更好的想法,并且也可以先拿到一部分的分数。

下面我们先从最基本的方法开始实现,再在此基础上进行优化算法。

70分解法

矩阵A我们通过一个二维数组来实现,注意此处应该使用double类型,因为计算均值涉及到除法,精度不够很可能会导致结果错误。

外层循环肯定是对整个数组进行遍历,内部进行每一个点邻域均值的计算。

首先应确定当前点的邻域范围,再对其内部进行求均值,此处应该考虑到边界的情况。(这里我直接用三目运算符看起来舒服点,用if也都是一样的)

//对于A[i][j],确定邻域范围:
//确定邻域的上下边界 
		int up = (i-r<0) ? 0 : (i-r);
		int down = (i+r>n-1) ? (n-1) : (i+r);
		
//确定邻域的左右边界 
			int right = (j+r>n-1) ? (n-1) : (j+r);
			int left = (j-r<0) ? 0 : (j-r);

确定边界后,对其中所有点求和再求均值,然后判断是否小于等于阈值,计数。

完整代码如下:

#include<cstdio>
double a[601][601] = {0};

int main() {
	int n, r;
	double L, t;
	scanf("%d %lf %d %lf", &n, &L, &r, &t);
	for(int i=0; i<n; i++) {
		for(int j=0; j<n; j++) {
			scanf("%lf", &a[i][j]);
		}
	}

	int count = 0;
	for(int i=0; i<n; i++) {
		//确定邻域的上下边界 
		int up = (i-r<0) ? 0 : (i-r);
		int down = (i+r>n-1) ? (n-1) : (i+r);
		for(int j=0; j<n; j++) {
			//确定邻域的左右边界 
			int right = (j+r>n-1) ? (n-1) : (j+r);
			int left = (j-r<0) ? 0 : (j-r);
			double sum = 0;
			for(int p=up; p<=down; p++) {
				for(int q=left; q<=right; q++) {
					sum += a[p][q]; //邻域内所有数求和 
				}
			}
			double cr = (double)down-up+1;
			double cl = (double)right-left+1;
		    double average = sum/(cr*cl); //求均值 
			if(average<=t) count++;
		}
	}
	
	printf("%d", count);
	return 0;
}

在这里插入图片描述
果然,运行超时,只有70分,说明解题思路是正确的,但是数据较大时会运行超时。这时我们对于程序主体是不用大改的,应该寻找程序的多重循环中哪里可以优化,减少执行次数。

满分解法

回顾一下上面的程序,可以确定的是外层循环,即遍历整个矩阵,求每一个点的邻域均值是不能再优化的,因为要求满足要求的点的个数,必须要计算每一个点的邻域均值,那么就需要去优化内部求均值的过程。

在求均值的过程中,上面我们是对每一个邻域内的点都遍历一边求和再计算均值的,这样确实执行次数非常多,并且我们很容易发现各个相邻的邻域中有很多点都是重复的,每一次都重新计算总和很浪费时间。所以我们应该利用这些重复的部分来减少循环次数。

以样例1为例,我们可以看到,如果求的是 A [ 2 ] [ 2 ] A[2][2] A[2][2]的邻域内总和(即下图中红色矩形中的总和),其实可以看成是最外层的大矩形内总和,减去相邻两个小矩形内的总和。依次类推,所有的邻域都可以用这种方法来计算。
在这里插入图片描述
因此,这里我们可以通过前缀和的方法来对二维数组先进行预处理,将每个 A [ i ] [ j ] A[i][j] A[i][j]的值计算为从最左上角的 A [ 0 ] [ 0 ] A[0][0] A[0][0] A [ i ] [ j ] A[i][j] A[i][j]的一个矩形内的所有数之和。(以上图样例1为例, A [ 3 ] [ 3 ] A[3][3] A[3][3]即为 0 + 1 + 2 + 3 + . . . + 15 0+1+2+3+...+15 0+1+2+3+...+15)。

然后在计算邻域内总和时,只需通过上面描述的方法即可快速求出结果,不必再对邻域内的数进行遍历。

//对于A[i][j],确定邻域范围:
//确定邻域的上下边界 
		int up = (i-r<0) ? 0 : (i-r);
		int down = (i+r>n-1) ? (n-1) : (i+r);
		
//确定邻域的左右边界 
		int right = (j+r>n-1) ? (n-1) : (j+r);
		int left = (j-r<0) ? 0 : (j-r);
			
		double sum = a[down][right] - a[down][left-1] - a[up-1][right] + a[up-1][left-1];

然后我还在整个矩阵的外围补了0,以方便计算,就无需对边界情况进行额外的特殊处理。

最后完整的满分代码如下:

#include<cstdio>
double a[601][601] = {0};

int main() {
	int n, r;
	double L, t;
	scanf("%d %lf %d %lf", &n, &L, &r, &t);
	//从下标1开始输入,即给矩阵外围补了一圈0,方便下面计算前缀和,无需考虑边界情况 
	for(int i=1; i<=n; i++) {
		for(int j=1; j<=n; j++) {
			scanf("%lf", &a[i][j]);
		}
	}
	
	//求出每个点a[i][j]坐标为边界的i*j的矩阵内元素值的总和 
	for(int i=1; i<=n; i++) {
		for(int j=1; j<=n; j++) {
			a[i][j] += a[i-1][j] + a[i][j-1] - a[i-1][j-1];
		}
	}
	
	int count = 0;
	for(int i=1; i<=n; i++) {
		//确定邻域的上下边界 
		int up = (i-r<1) ? 1 : (i-r);
		int down = (i+r>n) ? n : (i+r);
		for(int j=1; j<=n; j++) {
			//确定邻域的左右边界 
			int right = (j+r>n) ? n : (j+r);
			int left = (j-r<1) ? 1 : (j-r);
			//求均值 
			double sum = a[down][right] - a[down][left-1] - a[up-1][right] + a[up-1][left-1];
			double cr = (double)down-up+1;
			double cl = (double)right-left+1;
		    double average = sum/(cr*cl);
			if(average<=t) count++;
		}
	}
	
	printf("%d", count);
	return 0;
}

在这里插入图片描述

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

CCF CSP 2021-04-2 邻域均值 题解及满分代码(C++11) 的相关文章

  • 2021-03-19

    输出 数字直角三角形 1 2 3 4 5 6 7 8 9 10 11 12 可根据需要增加行数 public class trangle 64 param args public static void main String args T
  • 2021-03-19

    switch语句实现成绩选择 注意强制转换 import java util Scanner public class Grade Switch 64 param args public static void main String ar
  • 2021-08-30 创建tensor时,注意不要让梯度消失了

    下面这种是错误的 xff0c 梯度会消失 data span class token operator 61 span torch span class token punctuation span tensor span class to
  • OpenSSH权限提升漏洞(CVE-2021-41617)修复 Centos 7升级Openssh 8.8

    OpenSSH权限提升漏洞 xff08 CVE 2021 41617 xff09 修复 1 准备工作2 安装必须的包3 下载OpenSsh 8 8p14 OpenSsh 解压安装5 配置文件修改6 重启服务7 意外 Centos 7升级Op
  • 2021-06-18

    AttributeError module torch functional has no attribute relu AttributeError module torch functional has no attribute rel
  • Daily practice——2021/1/31

    1 函数若无返回值 则它一定无形参 请问这个说法是正确的吗 xff1f 答 xff1a 这个说法不正确 一个函数可以有参数 xff0c 没有返回值 xff1b 可以没有参数 xff0c 有返回值 xff1b 可以没参数 xff0c 没返回值
  • 2021-08-19-leetcode-00001

    二分查找 704 给定一个 n 个元素有序的 xff08 升序 xff09 整型数组 nums 和一个目标值 target xff0c 写一个函数搜索 nums 中的 target xff0c 如果目标值存在返回下标 xff0c 否则返回
  • 【CCF推荐专区】计算机类优质SCI&EI好刊,期刊质量高,部分期刊仅有少量版面

    x1f308 智能传感类 xff08 TOP xff09 CCF C类 期刊简介 IF 7 0 8 0 xff0c JCR1区 xff0c 中科院2 1区 检索情况 SCI amp EI 双检 xff0c 正刊 xff0c CCF C类 征
  • 2021-03-08

    大疆无人机自己动手更换电芯的注意事项 xff0c 当电池多电芯出现均大压差且调整数据无效后 xff0c 或发现某块或多块电芯鼓包 xff0c 说明电芯已经老化 xff0c 寿命用尽 xff0c 就需要更换电芯了 xff0c 厂家为保护消费者
  • 2021-11-11 机械臂路径规划学习进展

    机械臂关节空间和末端空间路径规划 关节空间路径规划简单障碍物情况 xff1a 之后搭建复杂障碍物场景 xff1a 测试发现路径规划的两个步骤 xff1a 采用了关节空间进行路径规划的方案 xff0c 原因主要是在关节空间也就是构型空间中 x
  • 2021-05-14 Redis面试题 redis 部署生产环境

    redis 部署生产环境 redis cluster xff0c 10 台机器 xff0c 5 台机器部署了 redis 主实例 xff0c 另外 5 台机器部署了 redis 的从实例 xff0c 每个主实例挂了一个从实例 xff0c 5
  • 2021-01-18

    求助 xff0c 关于Ubuntu20 04安装网络调试助手打不开的问题 我在虚拟机上安装了Ubuntu20 04并安装了网络调试助手 xff0c 但却打不开 xff0c 运用了sudo apt get libqtgui4 amd64也没用
  • 2021-08-10

    LEGO loam第一次测试运行数据包nsh indoor outdoor成功 xff1a 记录以下 xff0c 以免自己忘记步骤 在第一个终端里 xff1a 1 source catkin ws devel setup bash xff0
  • 【202206-3】角色授权

    AC的快乐无与伦比 本蒟蒻刚看到这道题时 就被超长的题干和复杂的关系唬住了 于是学习了各路大神的解法 终于AC 成功照虎画猫了 现将在此过程中学到的种种知识总结如下 作为本小白菜 不但小白还有菜 的编程笔记 Attention 一 C 中的
  • 2021美赛 MCM\ICM D题

    自古以来 音乐就已成为人类社会的一部分 已成为文化遗产的重要组成部分 为了理解音乐在人类集体经验中所扮演的角色 我们被要求开发一种量化音乐发展的方法 在创作新音乐时 有许多因素会影响艺术家 包括其天赋的创造力 当前的社会或政治事件 使用新乐
  • C++语言基础--递归函数

    对于很多编程初学者来说 递归算法是学习语言的最大障碍之一 可能也有一大部分人知道递归 也能看的懂递归 但在实际做题过程中 却不知道怎么使用 递归的定义 1 很官方的说法 递归 在数学与计算机科学中 是指在函数的定义中使用函数自身的方法 也就
  • CCF/CSP 201409-3 字符串匹配(满分题解Java版)

    此题虽然放在了第三题 但是如果对Java的API了解的比较好的同学 解这道题一点都不难 比前几题都要简单一些 题目描述 官方题目地址 读题请点击 Java满分题解 import java util Scanner next 与 nextLi
  • 数据结构--二叉堆与优先队列

    堆的一些性质 1 堆是一颗完全二叉树 2 堆的顶端一定是 最大 最小 的 但是要注意一个点 这里的大和小并不是传统意义下的大和小 它是相对于优先级而言的 3 堆一般有两种样子 小根堆和大根堆 分别对应第二个性质中的 堆顶最大 堆顶最小 对于
  • CSP 202212-1 现值计算

    答题 主要就是 include
  • 第十六次CCF认证模拟试题(201903-2):二十四点(Java完整版)

    最近在练习算法 觉得CCF的算法题都还不错 就做了一下子 试卷原题 Java版解法 import java util ArrayList import java util Scanner public class Main public s

随机推荐