文章目录
前言
本章节主要讲述使用数组存放运算的结果。
学习路线:C++从入门到NOI学习路线
学习大纲:C++全国青少年信息学奥林匹克竞赛(NOI)入门级-大纲
上一站:C++排序算法入门之计数排序
下一站:
一、导入
想象一下,你要统计一本书中每一页数字0到9出现的次数,就像我们在做手工计数一样。为了方便记录,我们准备一个小工具——一个有10个小格子的盒子,每个小格子对应一个数字(从0到9)。每当我们在书页上看到某个数字时,就去找到对应数字的小格子,然后在那个小格子里放一个小石子代表这个数字出现了一次。
具体步骤如下:
- 准备一个“神奇”的盒子,它有10个格子,分别标记为0到9。
- 开始翻阅书页,每翻到一页,就查看这页的页码。
- 把当前页码的每一位数字拿出来看,比如看到的是数字3,就去找到标记为3的那个格子,然后往里面放一个小石子(在程序中就是增加计数值1)。
- 继续查看这一位后面的数字,直到没有数字为止,再翻到下一页重复步骤3。
- 当统计完所有页码后,每个格子中的小石子数量就代表了相应数字在所有页码中出现的次数。
在C++编程中,我们可以用一个大小为10的数组代替这个“神奇”盒子,数组的每个元素对应一个数字,初始值都设置为0。在遍历页码的过程中,提取页码的每一位,然后增加对应数组元素的值即可。
以下是简化版的代码示例:
#include<iostream>
using namespace std;
int main() {
// 创建一个大小为10的数组,就像我们的"神奇盒子"
int digitCounts[10] = {0}; // 初始化所有元素为0
// 假设我们要统计1到1000这些页码
for(int pageNum = 1; pageNum <= 1000; ++pageNum) {
// 分解页码的每一位数字
while (pageNum > 0) {
int digit = pageNum % 10; // 得到页码的个位数
digitCounts[digit]++; // 在对应数组位置加1,表示该数字出现一次
pageNum /= 10; // 移除已经统计过的个位数
}
}
// 打印每个数字出现的次数
for(int i = 0; i < 10; ++i) {
cout << "数字 " << i << " 出现了 " << digitCounts[i] << " 次" << endl;
}
return 0;
}
二、例题讲解
问题一:1178 - COUNT
类型:数组问题
题目描述:
一本书的页数为 N ,页码从 1 开始编起,请你求出全部页码中,用了多少个0,1,2…9 。
输入:
一个正整数 N ( N≤10000 ),表示总的页码。
输出:
共十行:第 k 行为数字 k−1 的个数。
样例:
输入:
11
输出:
1
4
1
1
1
1
1
1
1
1
1.分析问题
- 已知:页数为N
- 未知:用了多少个0,1,2…9。
- 关系:计数
2.定义变量
- int n用来接收用户输入的页数;a[10]是一个长度为10的整型数组,初始化为全0,用于存储0-9每个数字出现的次数;temp作为临时变量在计算过程中使用。
int n,a[10]={0},temp;
3.输入数据
- 从标准输入流(通常是键盘)读取一个整数并赋值给变量n。
cin>>n;
4.数据计算
- 使用一个外层循环,当n大于0时执行循环体。
- 在循环体内,将当前的n值赋给临时变量temp。
- 内层循环处理temp的每一位数字,通过取模运算得到个位数i,并将对应位置a[i]的计数值加1,然后将temp除以10去除个位数。
- 循环结束后,n减1,继续处理下一个整数。
//四、数据计算
// 当n大于0时,循环执行以下操作
while(n>0){
// 将当前页数n暂存到临时变量temp中
temp=n;
// 对temp的每一位数字进行处理
while(temp>0){
// 获取temp的个位数
int i=temp%10;
// 把该位数出现的次数累加到对应的数组元素上
a[i]+=1;
// 将temp除以10去掉个位数,以便处理下一位
temp/=10;
}
// 减少一页,准备处理下一页
--n;
}
5.输出结果
- 遍历数组a,依次输出0-9每个数字出现的次数。
- 主函数返回0,表示程序正常结束。
//五、输出结果
// 遍历数组a,依次输出0~9每个数字出现的次数
for(int i=0;i<10;i++){
cout<<a[i]<<endl;
}
return 0;
完整代码如下:
#include<iostream>
using namespace std;
int main(){
//一、分析问题
// 已知条件:有一个整数变量n代表页数
// 未知条件:需要统计从1到n(含n)这些整数中,各个数字0~9出现的次数
// 关系:对每个整数的每一位进行计数
//二、数据定义
// 定义一个整数变量n用于接收用户输入的页数
// 初始化一个大小为10的整型数组a,用于记录0~9每个数字出现的次数
// 定义一个临时变量temp,在计算过程中暂存数值
int n,a[10]={0},temp;
//三、数据输入
cin>>n;
//四、数据计算
// 当n大于0时,循环执行以下操作
while(n>0){
// 将当前页数n暂存到临时变量temp中
temp=n;
// 对temp的每一位数字进行处理
while(temp>0){
// 获取temp的个位数
int i=temp%10;
// 把该位数出现的次数累加到对应的数组元素上
a[i]+=1;
// 将temp除以10去掉个位数,以便处理下一位
temp/=10;
}
// 减少一页,准备处理下一页
--n;
}
//五、输出结果
// 遍历数组a,依次输出0~9每个数字出现的次数
for(int i=0;i<10;i++){
cout<<a[i]<<endl;
}
return 0;
}
问题二:1180 - 数字出现次数
类型:数组问题
题目描述:
有 50个数( 0∼19),求这 50个数中相同数字出现的最多次数为几次?
输入:
50 个数字。
输出:
1 个数字(即相同数字出现的最多次数)。
样例:
输入:
1 10 2 0 15 8 12 7 0 3 15 0 15 18 16 7 17 16 9 1 19 16 12 17 12 4 3 11 1 14 2 11 14 6 11 4 6 4 11 13 18 7 0 3 2 3 18 19 2 16
输出:
4
1.分析问题
- 已知:有50个整数。
- 未知:统计0到19之间每个数字在这50个数中出现的次数。
- 关系:找出这些数字中出现次数最多的次数是多少。
2.定义变量
- 定义一个长度为20的数组a,用于存储0-19每个数字出现的次数,默认初始化为0。
- 定义一个长度为50的数组b,用于存储用户输入的50个整数。
- 定义一个临时变量temp,用于暂存当前最大出现次数,并初始化为0
int a[20] = {};
int b[50];
int temp;
3.输入数据
- 从标准输入读取50个整数并存入数组b,同时更新数组a中相应数字的计数
for(int i = 0; i < 50; i++){
cin >> b[i];
if(b[i] >= 0 && b[i] < 20){ // 只统计0-19的整数出现次数,超出范围的数字不计入
a[b[i]] += 1;
}
}
4.数据计算
- 初始化 temp 为数组 a 的第一个元素(即默认计数值为0)。
- 接下来遍历数组 a,比较每个元素的值与 temp,如果发现某个元素的值更大(即该数字出现次数更多),则更新 temp 为这个更大的值。
// 初始化temp为0,用于记录当前找到的最大出现次数
temp = a[0];
// 遍历数组a,查找出现次数最多的整数的次数
for(int i = 0; i < 20; i++){
if(temp < a[i]){ // 如果当前元素的计数大于temp,则更新temp为当前计数
temp = a[i];
}
}
5.输出结果
- 输出在这50个数中相同数字出现的最多次数
- 主函数返回0,表示程序运行成功
// 五、输出结果
cout << temp << endl;
return 0;
完整代码如下:
#include<iostream>
using namespace std;
int main(){
// 一、问题分析
// 已知:有50个整数存放在数组b中
// 任务:统计0到19之间每个数字在这50个数中出现的次数
// 目标:找出这些数字中出现次数最多的次数是多少
// 二、数据定义
// 定义一个长度为20的数组a,用于存储0-19每个数字出现的次数,默认初始化为0
int a[20] = {};
// 定义一个长度为50的数组b,用于存储用户输入的50个整数
int b[50];
// 定义一个临时变量temp,用于暂存当前最大出现次数,并初始化为0
int temp;
// 三、数据输入
// 从标准输入读取50个整数并存入数组b,同时更新数组a中相应数字的计数
for(int i = 0; i < 50; i++){
cin >> b[i];
if(b[i] >= 0 && b[i] < 20){ // 只统计0-19的整数出现次数,超出范围的数字不计入
a[b[i]] += 1;
}
}
// 四、数据计算
// 初始化temp为0,用于记录当前找到的最大出现次数
temp = a[0];
// 遍历数组a,查找出现次数最多的整数的次数
for(int i = 0; i < 20; i++){
if(temp < a[i]){ // 如果当前元素的计数大于temp,则更新temp为当前计数
temp = a[i];
}
}
// 五、输出结果
// 输出在这50个数中相同数字出现的最多次数
cout << temp << endl;
// 主函数返回0,表示程序运行成功
return 0;
}
思路二
读入50个元素,可以不用数组,可以用一个整数变量,每读入一个数,就计数一次(记录该数出现的次数要自增1)。
#include <iostream>
using namespace std;
int main(){
//存放每个元素
int x;
//用来存放每个数出现的次数
int c[20]=0};
int m;//m用来求最多的次数(c数组的最大值)
//读入50个数
for(int i=0;i<50;i++){
cin>>X;
//下标为x的c数组的元素要自增
c[x]++;
//求最多出现的次数(c数组的最大数)
m=c[0];
for(int i =1;i<20;i++){
if(c[i]> m){
m= c[i];
}
}
cout<<m<<endl;
}
问题三:1183 - 去除重复数字
类型:数组问题
题目描述:
给你 N 个数(N < 100),每个数都在(0 ~ 1000)之间,其中由很多重复的数字,请将重复的数字只保留一个,并将剩下的数由小到大排序并输出。
输入:
输入有 2 行;
第 1 行为 1个正整数,表示数的个数:N。
第 2 行有 N个用空格隔开的整数。
输出:
第 1 行为 1 个正整数 M,表示不相同数的个数。
接下来的 M 行,每行一个整数,表示从小到大排好序的不相同的数。
样例:
输入:
10
20 40 32 67 40 20 89 300 400 15
输出:
8
15
20
32
40
67
89
300
400
1.分析问题
- 已知:N个数。
- 未知:输出数列 。
- 关系:重复的数字只保留一个,并将剩下的数由小到大排序。
2.定义变量
- 定义一个足够大的数组a[],用于存储不重复的整数。
- 定义变量n接收输入的整数总数。
- 定义变量x用于读取单个输入的整数。
- 定义变量count记录当前不重复整数的数量。
- 定义布尔变量f,用于判断当前读取的整数是否已经在数组a[]中出现过。
int a[100],n,x,count=0;
bool f;
3.输入数据
- 先读取整数总数n。
- 使用一个外层循环,读取n个整数x。
- 对于每个读取到的整数x,使用一个内层循环遍历数组a[],检查x是否已经存在。
- 如果x不在数组a[]中,则将x添加到数组a[]中,并将count递增。
// 输入数的个数n
cin >> n;
// 读取n个数,并将其与数组a中的数进行比较,剔除重复项
for(int i = 0; i < n; i++){
cin >> x;
f = false; // 初始化f为false,表示当前数尚未发现重复
// 遍历数组a,检查x是否已存在于数组中
for(int j = 0; j < count; j++){
if(x == a[j]){ // 发现重复项
f = true;
break;
}
}
// 若x未在数组a中出现过,则将其添加进数组a,并增加count
if(f == false){
a[count] = x;
++count;
}
}
4.数据计算
- 对数组a[]中前count个不重复的整数进行冒泡排序,使其按升序排列。
// 对数组a中存储的不重复数进行排序(升序)
for(int i = 0; i < count; i++){
for(int j = 0; j < count - i - 1; j++){
if(a[j] > a[j + 1]){ // 交换较大的数和它后面较小的数
int temp = a[j];
a[j] = a[j + 1];
a[j + 1] = temp;
}
}
}
5.输出结果
// 输出数组a中不重复数的数量
cout << count << endl;
// 输出排序后的不重复数列
for(int i = 0; i < count; i++){
cout << a[i] << endl;
}
// 主函数返回0,表示程序执行成功
return 0;
完整代码如下:
#include<iostream>
using namespace std;
int main(){
// 一、问题分析
// 已知:需要处理一组整数序列,包含N个数
// 任务:输出去重后的数列,并按从小到大的顺序排列
// 二、数据定义
// 定义一个大小为100的整数数组a,用于存储唯一不重复的数
int a[100];
// 定义变量n,用于存储输入的数的个数
int n;
// 定义变量x,用于临时存储每次读入的数
int x;
// 定义变量count,用于记录数组a中已经存储了多少个不重复的数
int count = 0;
// 定义布尔变量f,用于标记当前读入的数是否已在数组a中出现过
bool f;
// 三、数据输入
// 输入数的个数n
cin >> n;
// 读取n个数,并将其与数组a中的数进行比较,剔除重复项
for(int i = 0; i < n; i++){
cin >> x;
f = false; // 初始化f为false,表示当前数尚未发现重复
// 遍历数组a,检查x是否已存在于数组中
for(int j = 0; j < count; j++){
if(x == a[j]){ // 发现重复项
f = true;
break;
}
}
// 若x未在数组a中出现过,则将其添加进数组a,并增加count
if(f == false){
a[count] = x;
++count;
}
}
// 四、数据计算
// 对数组a中存储的不重复数进行排序(升序)
for(int i = 0; i < count; i++){
for(int j = 0; j < count - i - 1; j++){
if(a[j] > a[j + 1]){ // 交换较大的数和它后面较小的数
int temp = a[j];
a[j] = a[j + 1];
a[j + 1] = temp;
}
}
}
// 五、输出结果
// 输出数组a中不重复数的数量
cout << count << endl;
// 输出排序后的不重复数列
for(int i = 0; i < count; i++){
cout << a[i] << endl;
}
// 主函数返回0,表示程序执行成功
return 0;
}
思路二
- 通过长度为1001的标记数组f来标记某个数是否出现过,判断x是否出现过,通过判断 f[x]是否为0来判断,如果f[x]=0,认为没有出现过,则将x存入数组 a,同时将f[x]标记为1。
#include<iostream>
using namespace std;
int main(){
// 一、分析问题
// 已知:程序将会接收N个整数
// 任务:生成一个新的数列,其中包含输入的所有不重复整数,并按照从小到大的顺序排列
// 二、数据定义
// 定义一个长度为1001的数组a,用作标记数组,数组索引代表整数,数组值代表该整数是否出现过
// 定义变量n用于接收整数的个数
// 定义变量x用于临时存储每次读入的整数
// 定义变量count用于统计不重复整数的数量,初始化为0
int a[1001]={0}, n, x, count=0;
// 三、数据输入
// 读取整数个数n
cin >> n;
// 遍历输入的n个整数
for(int i = 0; i < n; i++){
// 读取一个整数x
cin >> x;
// 判断整数x是否还未出现(通过a[x]==0判断)
if(0 == a[x]){
// 如果x未出现过,则计数器count加1
++count;
}
// 标记整数x已出现(将a[x]设置为1)
a[x] = 1;
}
// 四、数据计算
// 此处代码为空,因为在读取和标记的过程中已经完成了去重和排序的初步处理(标记法)
// 五、输出结果
// 输出不重复整数的数量
cout << count << endl;
// 遍历数组a,输出那些标记为1(即出现过的不重复整数)
for(int i = 0; i < 1001; i++){
if(1 == a[i]){
cout << i << endl;
}
}
// 主函数返回0,表示程序执行成功
return 0;
}
三、练习
问题一:1179 - 求N个整数的平均数、众数和中位数
问题二:1252 - 邮票组合
问题三:1334 - 扑克牌组合
四、总结
NOI(全国青少年信息学奥林匹克竞赛)中的试题常常涉及到数组的应用,数组在信息学竞赛中主要用于解决多种类型的问题,如存储中间结果、构建数据结构、实现算法等。
数组在存放运算结果方面的应用:
1. 存储序列化运算结果:
- 在进行一系列迭代或循环计算时,可以直接利用数组存储每个步骤的计算结果,便于后续分析或进一步处理。例如,模拟动态变化的过程、计算斐波那契数列、欧几里得算法等。
2. 实现数据结构:
- 线性结构:数组可以作为基础实现链表、队列、栈等线性数据结构,用于存储运算结果序列。
- 树形结构:数组可以紧凑地表示完全二叉树、满二叉树等,便于快速访问节点及其运算结果。
- 稀疏矩阵:对于稀疏矩阵,可以采用三元组形式(行号、列号、值)存储非零元素,并用数组存放这些三元组。
3. 状态压缩:
- 在处理具有组合性质的问题时,有时可以通过数组实现状态压缩,将多个状态信息合并到一个整数数组中,节省存储空间,加快运算速度。
4. 动态规划:
- 动态规划问题中,经常需要使用数组来存储子问题的最优解,从而避免重复计算,提高效率。例如背包问题、最长公共子序列、最短路径等问题。
5. 排序与查找:
- 在排序算法中,如冒泡排序、选择排序、插入排序等,通常会用数组存放待排序元素以及排序过程中的中间结果。同时,数组也是实现查找算法(如二分查找)的基础结构。
6. 图论问题:
- 在图论问题中,邻接矩阵或邻接表通常用数组实现,用于存储图的结构和进行各种图的遍历和运算。
7. 哈希表:
- 虽然不是严格意义上的数组,但在NOI中常常用数组结合合适的哈希函数构造简单的哈希表,用于快速存取运算结果或者查找关键信息。
总之,在NOI竞赛中,数组是一种极其重要的工具,通过巧妙运用数组,可以有效地解决问题,实现高效算法。
五、感谢
如若本文对您的学习或工作有所启发和帮助,恳请您给予宝贵的支持——轻轻一点,为文章点赞;若觉得内容值得分享给更多朋友,欢迎转发扩散;若认为此篇内容具有长期参考价值,敬请收藏以便随时查阅。
每一次您的点赞、分享与收藏,都是对我持续创作和分享的热情鼓励,也是推动我不断提供更多高质量内容的动力源泉。期待我们在下一篇文章中再次相遇,共同攀登知识的高峰!