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年8月】解决 rosdep update超时问题

    修改两个文件即可快速解决超时问题 1 修改 etc ros rosdep sources list d 20 default list 执行sudo gedit etc ros rosdep sources list d 20 defaul
  • 2021-10-24(机器学习实战-ch09 map方法和int不兼容问题)

    机器学习实战 ch09 TypeError unsupported operand type s for map and int span class token operator gt gt span span class token o
  • 10 个 GitHub 上最火的程序员简历项目,2021 金三银四必备!

    大家好 xff0c 我是你们的 猫哥 xff0c 一个不喜欢吃鱼 又不喜欢喵 的超级猫 前言 猫哥是一个常年混迹在 GitHub 上的猫星人 xff0c 所以发现了不少好的前端开源项目 常用技巧 xff0c 在此分享给大家 公众号 xff1
  • 2021-02-18

    多旋翼飞行器学习笔记 二 机架设计 2 1布局设计 1 机身基本布局 交叉型 xff1a 目前常用的是X字型布局 xff0c 因为 xff1a xff08 1 xff09 机动性更强 xff1b xff08 2 xff09 前视相机的视场角
  • 2021-03-08

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

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

    大华面试 xff1a 一面和二面 一面 xff1a 首先自我介绍 1 序列化的使用方式以及情景 2 Springboot的启动过程 3 Mysq中lB 43 树和B树索引区别 xff0c 聚簇索引和非聚簇索引区别 4 Spring中bean
  • 2021校招_海康威视

    2021届海康威视面试 一面 xff1a 1 https与http协议的区别 2 Spring的启动过程 3 Springboot相比较Spring的优点 4 Linux修改文件权限命令 5 项目中所用到的技术 6 Restful风格 7
  • 2021校招_思科

    思科给我发的太晚了 xff0c 十一月份才给我消息 思科一面凉凉 主要是针对你的简历 问到我的主要内容包括 xff1a 数据库设计 xff0c 是否使用到设计模式 xff0c 以及遇到问题如何解决 包括ngnix xff0c redis h
  • 2021-01-11

    C 43 43 指针随便笔记 sizeof 先说一个没有成员函数和参数的类 xff0c 占用一个字节 类中的成员函数 xff0c 作为外部指针时 xff0c 需要记得delete xff0c 否则会内存泄漏 指针的sizeof是指针本身的数
  • 【202206-3】角色授权

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

    坐标点target 中心点center 角度angle 旋转后坐标 function getRotatePoint targetX targetY centerX centerY angle const rotation angle Mat
  • C++语言基础--递归函数

    对于很多编程初学者来说 递归算法是学习语言的最大障碍之一 可能也有一大部分人知道递归 也能看的懂递归 但在实际做题过程中 却不知道怎么使用 递归的定义 1 很官方的说法 递归 在数学与计算机科学中 是指在函数的定义中使用函数自身的方法 也就
  • CCF-CSP真题《202305-1 重复局面》思路+python,c++满分题解

    想查看其他题的真题及题解的同学可以前往查看 CCF CSP真题附题解大全 试题编号 202305 1 试题名称 重复局面 时间限制 1 0s 内存限制 512 0MB 问题描述 题目背景 国际象棋在对局时 同一局面连续或间断出现3次或3次以
  • ES6 Symbol

    概览 const mySymbol Symbol mySymbol console log mySymbol Symbol mySymbol console log mySymbol Symbol mySymbol false consol
  • 解决 Mac 左滑浏览器默认的返回事件

    阻止 document body style overscrollBehaviorX none 恢复 document body style overscrollBehaviorX auto 参考 https juejin cn post
  • ccf-201412-3 集合竞价(详解)

    ccf 201412 3 集合竞价 详解 试题编号 201412 3 试题名称 集合竞价 时间限制 1 0s 内存限制 256 0MB 问题描述 问题描述 某股票交易所请你编写一个程序 根据开盘前客户提交的订单来确定某特定股票的开盘价和开盘
  • Pixi.js 显示文字无法换行

    官方案例 message style wordWrap true wordWrapWidth 100 align center 中文无法换行 设置breakWords属性 sprite style wordWrap true wordWra
  • CCF-CSP-202109-4-收集卡牌

    原题链接 满分代码 include

随机推荐