CCF CSP 2021-09-2 非零段划分 题解及满分代码(C++11)

2023-05-16

文章目录

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

问题描述

题目描述

A 1 , A 2 , ⋯ , A n A_1,A_2,⋯,A_n A1,A2,,An 是一个由 n 个自然数(非负整数)组成的数组。我们称其中 Ai,⋯,Aj 是一个非零段,当且仅当以下条件同时满足:

  • 1 ≤ i ≤ j ≤ n 1≤i≤j≤n 1ijn
  • 对于任意的整数 k k k,若 i ≤ k ≤ j i≤k≤j ikj,则 A k > 0 A_k>0 Ak>0
  • i = 1 i=1 i=1 A i − 1 = 0 A_i−1=0 Ai1=0
  • j = n j=n j=n A j + 1 = 0 A_j+1=0 Aj+1=0

下面展示了几个简单的例子:

  • A = [ 3 , 1 , 2 , 0 , 0 , 2 , 0 , 4 , 5 , 0 , 2 ] A=[3,1,2,0,0,2,0,4,5,0,2] A=[3,1,2,0,0,2,0,4,5,0,2] 中的 4 个非零段依次为 [3,1,2]、[2]、[4,5] 和 [2];
  • A = [ 2 , 3 , 1 , 4 , 5 ] A=[2,3,1,4,5] A=[2,3,1,4,5] 仅有 1 个非零段;
  • A = [ 0 , 0 , 0 ] A=[0,0,0] A=[0,0,0] 则不含非零段(即非零段个数为 0)。

现在我们可以对数组 A 进行如下操作:任选一个正整数 p,然后将 A 中所有小于 p 的数都变为 0。试选取一个合适的 p,使得数组 A 中的非零段个数达到最大。若输入的 A 所含非零段数已达最大值,可取 p=1,即不对 A 做任何修改。

输入格式

从标准输入读入数据。

输入的第一行包含一个正整数 n。

输入的第二行包含 n 个用空格分隔的自然数 A 1 , A 2 , ⋯ , A n A_1,A_2,⋯,A_n A1,A2,,An

输出格式

输出到标准输出。

仅输出一个整数,表示对数组 A 进行操作后,其非零段个数能达到的最大值。

样例1输入

11
3 1 2 0 0 2 0 4 5 0 2

样例1输出

5

样例1解释

p=2 时,A=[3,0,2,0,0,2,0,4,5,0,2],5 个非零段依次为 [3]、[2]、[2]、[4,5] 和 [2];此时非零段个数达到最大。

样例2输入

14
5 1 20 10 10 10 10 15 10 20 1 5 10 15

样例2输出

4

样例2解释

p=12 时,A=[0,0,20,0,0,0,0,15,0,20,0,0,0,15],4 个非零段依次为 [20]、[15]、[20] 和 [15];此时非零段个数达到最大。

样例3输入

3
1 0 0

样例3输出

1

样例3解释

p=1 时,A=[1,0,0],此时仅有 1 个非零段 [1],非零段个数达到最大。

样例4输入

3
0 0 0

样例4输出

0

样例4解释

无论 p 取何值,A 都不含有非零段,故非零段个数至多为 0。

子任务

70% 的测试数据满足 n ≤ 1000 n≤1000 n1000

全部的测试数据满足 n ≤ 5 × 1 0 5 n≤5×10^5 n5×105,且数组 A 中的每一个数均不超过 1 0 4 10^4 104

问题分析

已知:

(1) 非零段:数组中连续的不为0的一段数字(可以是单个数)

(2) 从A中选取p,将p中小于它的数都变为0,则A中的非零段个数会发生变化

求:A中非零段个数能达到的最大值

我们容易想到,对A中的值逐个选取成为p,再按要求对A中的数进行置0操作,每次完成后统计一下非零段个数,最后就可以得到最大值了。

所以我们先将统计非零段个数和根据p值置0的操作封装成单独的函数:

//求出数组的非零段个数 
int count(int n) {
	int k = 0;
	if(a[0]!=0) k++;
	for(int i=1; i<n; i++) {
		if(a[i-1] != 0)  continue;
		else {
			if(a[i] != 0)	k++;
		}
	}
	return k;
}

在这里为了方便计算,我将p值直接选取为A中的数组值,对小于等于p的数全部置0。

//将数组内小于等于p的数全部变为0 
void change(int p, int n) {
	for(int i=0; i<n; i++) {
		if(a[i]<=p) a[i]=0;
	}
}

但如果仅是通过遍历A来选取p值,不仅需要考虑数值重复出现的问题,并且每次都需要重新根据A的数据重新建立一个数组再置0再统计非零段个数。这样来实现一定会超时,并且代码撰写也很复杂。

进一步想,我们在置0时是将小于p的数都置0,如果我们选取p时是从小到大选取,那么每一次置0的过程就可以直接在上一次的基础上执行,可以节省时间。

因此,我们只要事先知道A中出现的数字从小到大有哪些就可以,这个可以用STL中的set容器来实现,在我们输入A数组的时候同时将数据插入set,内部会自动去重并排序。

70分解法

根据以上的思路,我们来进行代码的实现。(虽然也只有70分,但思路没啥问题)

#include<cstdio>
#include<cmath>
#include<set>
using namespace std;
int a[500010];
//求出数组的非零段个数 
int count(int n) {
	int k = 0;
	if(a[0]!=0) k++;
	for(int i=1; i<n; i++) {
		if(a[i-1] != 0)  continue;
		else {
			if(a[i] != 0)	k++;
		}
	}
	return k;
}

//将数组内小于等于p的数全部变为0 
void change(int p, int n) {
	for(int i=0; i<n; i++) {
		if(a[i]<=p) a[i]=0;
	}
}

int main() {
	int n;
	set<int> setA;
	scanf("%d", &n);
	for(int i=0; i<n; i++) {
		scanf("%d", &a[i]);
		setA.insert(a[i]);
	}
	int maxNum = count(n); //非零段个数的最大值 
	//从小到大对p取值,并将数组a内小于等于p的数都变为0,再计算非零段个数 
	set<int>::iterator it = setA.begin();
	while(it != setA.end()) {
		change(*it, n);
		int num = count(n);
		if(num>maxNum)  maxNum = num;
		it++;
	}
	printf("%d", maxNum);
	return 0;
}

在这里插入图片描述

代码只有70分,运行超时,说明循环执行次数太多,需要优化。

满分解法

分析上述解法的运行过程,思路方向是正确的,那么就应该对反复运行的循环语句优化,看看能不能减少一些运行次数。

外层循环对p取值是没法再减少的,因为要得出非零段最大值肯定要对p所有情况进行计算(也有可能有更好的方法但是我想不到了)

再看内层循环,每次选取p之后都要从头到尾遍历两次——置0和统计非零段,应该有省时的方法。

  1. 对于非零段的计算,我们是每次都从头到尾重新计算的,但其实再思考一下非零段的定义,其实我们不难发现:要让非零段增多其实就是将一个长非零段变成两个短的,即将中间的数变为0。这样一来,其实再置0操作的同时我们就可以计算出非零段个数是否有变化,即:如果当前位的前后及其自身都不为0,将当前位置0就会使非零段+1;同样的,如果当前位的前后都为0,自身不为0,那么将当前位置0就会使非零段-1。

  2. 对于置0的操作,我们每一次都是从头到尾遍历A,找到符合条件的值置0来实现的。对此,我们可以用空间换时间,提前将每个数字出现的位置索引存储下来,在置0时就无需再去遍历寻找。 在这里我用STL中的vector容器来实现,定义一个vector类型的数组来存储每个数字出现的位置索引(类似于散列表的思想)。

    vector<int> pos[10001];
    
    //在输入的同时存储所在位置
    for(int i=1; i<=n; i++) {
    		scanf("%d", &a[i]);
    		setA.insert(a[i]);
    		pos[a[i]].push_back(i); //存放当前数所在的位置索引  
    	}
    

    注意,这里的数组不能定义的太大,题目中提示数组 A 中的每一个数均不超过 1 0 4 10^4 104,所以我们将大小定为10001即可,太大会导致运行错误。

下面是最终具体的代码实现:

(在这里我将数组A的头和尾都补一个0,这样可以方便统计非零段,省略的头尾的特殊判断处理)

#include<cstdio>
#include<cmath>
#include<set>
#include<vector>
using namespace std;
int a[500010];
vector<int> pos[10001]; //存放每个数对应在数组中的索引

//求出数组的非零段个数 
int count(int n) {
	int k = 0;
	//由0到非0时,非零段个数+1 
	for(int i=1; i<=n; i++) {
		if(a[i-1]==0 && a[i]>0) k++;  
	}
	return k;
}

int main() {
	int n;
	set<int> setA; //存放数组中出现过的数 
	scanf("%d", &n);
	a[0] = a[n+1] = 0; //头尾补0,省去对头尾进行特殊判断 
	for(int i=1; i<=n; i++) {
		scanf("%d", &a[i]);
		setA.insert(a[i]);
		pos[a[i]].push_back(i); //存放当前数所在的位置索引  
	}
	
	int maxNum, temp; //非零段个数的最大值和当前值 
	maxNum = temp = count(n);
	
	//从小到大对p取值: 
	set<int>::iterator iterator = setA.begin();
	if(*iterator == 0) iterator++;
	while(iterator != setA.end()) {
		int p = *iterator;		
		for(vector<int>::iterator it=pos[p].begin(); it!=pos[p].end(); it++) {
			int index = *it; //取出位置索引 
			a[index] = 0;   //将该位置0 
			if( (a[index-1]>0) && (a[index+1]>0) ) temp++; //前后两个数都不为0时,非零段+1
			if( (a[index-1]==0) && (a[index+1]==0) ) temp--; //前后两个数都为0时,非零段-1
		}
        if(temp > maxNum) maxNum = temp;
		iterator++;
	}
	printf("%d", maxNum);
	return 0;
}

在这里插入图片描述

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

CCF CSP 2021-09-2 非零段划分 题解及满分代码(C++11) 的相关文章

  • 2021-03-18

    包络面与载波信号的确定
  • CSP认证 201803-3 URL映射

    CSP认证 201803 3 URL映射 链接 xff1a http 118 190 20 162 view page gpid 61 T71 题意 xff1a 从简条件下的 U R L 映 射 URL映射
  • 【2021最新版】JavaOOP面试题总结(99道题含答案解析)

    文章目录 1 什么是B S架构 xff1f 什么是C S架构2 Java都有那些开发平台 xff1f 3 什么是JDK xff1f 什么是JRE 4 Java语言有哪些特点5 面向对象和面向过程的区别6 什么是数据结构 xff1f 7 Ja
  • 位姿图优化小记2021.10.18

    1 场景描述 现在有一个小车在运动 xff0c 并搭载相机或激光雷达进行建图工作 xff0c 由于SLAM的作用 xff0c 在建图的同时小车也可以进行自身的定位 xff0c 因此建立的地图的参考都是相对于自身坐标系的 xff0c 也就是每
  • 2021-08-19-leetcode-00001

    二分查找 704 给定一个 n 个元素有序的 xff08 升序 xff09 整型数组 nums 和一个目标值 target xff0c 写一个函数搜索 nums 中的 target xff0c 如果目标值存在返回下标 xff0c 否则返回
  • CSP-S 模拟测试 51 题解

    考试过程 xff1a 惯例先看一遍三道题 xff0c T1 一开始反应要求割点 xff0c 但是这是有向图 xff0c 肯定不能求割点 xff0c 康了一下数据范围 xff0c 有40 是树的 xff0c 还不错 xff0c 决定待会在打
  • 2021-02-18

    多旋翼飞行器学习笔记 二 机架设计 2 1布局设计 1 机身基本布局 交叉型 xff1a 目前常用的是X字型布局 xff0c 因为 xff1a xff08 1 xff09 机动性更强 xff1b xff08 2 xff09 前视相机的视场角
  • 2021电赛F题视觉教程+代码免费开源

    2021电赛F题视觉教程 43 代码免费开源 最近好多要电赛题的源码 xff0c 其他csdn营销号下载都需要会员或钱 xff0c 正好最近课设又要做一遍电赛小车题 xff0c 哥们先把代码开源了 xff0c 饿死营销号 电赛宝藏链接 xf
  • 2021-11-11 机械臂路径规划学习进展

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

    舵机PWM信号转继电器开关信号 本文由 麦粒电子 撰写 xff0c 并提供相应产品服务 叙述 航模玩家经常需要DIY改装 譬如飞行器做一个投弹的开关 xff0c 船用模型做一个投食机关 再或者弄一些彩灯控制 往往这些功能只需要有一个简单的开
  • 2021校招_满帮(运满满)

    一面 xff08 电话面 xff09 xff1a 25min 1 询问HashMap相关结构以及原理 2 红黑树的基本结构 xff0c 以及什么时候会LL xff08 左转 xff09 3 Spring如何解决循环依赖的 4 Redis缓存
  • 2021-08-10

    LEGO loam第一次测试运行数据包nsh indoor outdoor成功 xff1a 记录以下 xff0c 以免自己忘记步骤 在第一个终端里 xff1a 1 source catkin ws devel setup bash xff0
  • 求旋转后的坐标

    坐标点target 中心点center 角度angle 旋转后坐标 function getRotatePoint targetX targetY centerX centerY angle const rotation angle Mat
  • CCF CSP 中国计算机学会-CCF计算机软件能力认证(计算机水平测试)-简介-详情

    CCF gt gt 简介 中国计算机学会 英文名为China Computer Federation 简称CCF 是由从事计算机及相关科学技术领域的科研 教育 开发 生产 管理 应用和服务的个人及单位自愿结成 依法登记成立的全国性 学术性
  • CCF-CSP真题《202303-1 田地丈量》思路+python,c++,java满分题解

    想查看其他题的真题及题解的同学可以前往查看 CCF CSP真题附题解大全 试题编号 202303 1 试题名称 田地丈量 时间限制 1 0s 内存限制 512 0MB 问题描述 问题描述 西西艾弗岛上散落着 n 块田地 每块田地可视为平面直
  • CCF/CSP 201409-3 字符串匹配(满分题解Java版)

    此题虽然放在了第三题 但是如果对Java的API了解的比较好的同学 解这道题一点都不难 比前几题都要简单一些 题目描述 官方题目地址 读题请点击 Java满分题解 import java util Scanner next 与 nextLi
  • 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
  • CSP 202209-1 如此编码

    答题 题目就是字多 include

随机推荐