image

编辑人: 沉寂于曾经

calendar2025-07-24

message4

visits366

2013年第十九届NOIP信息学奥赛提高组初赛C++试题答案及解析

一、单选题

1、一个32 位整型变量占用( )个字节。 

A 4

B 8

C 32

D 128

解析:【喵呜刷题小喵解析】:在大多数现代计算机系统中,一个字节(Byte)由8位(bit)组成。因此,一个32位的整型变量会占用4个字节。所以,正确答案是A选项,即4个字节。

2、二进制数11.01在十进制下是( )。 

A 3.25

B 4.125

C 6.25

D 11.125

解析:【喵呜刷题小喵解析】
二进制数11.01在十进制下的转换过程如下:

1. 对于整数部分11:
* 二进制11等于十进制的 1×2^1 + 1×2^0 = 2 + 1 = 3。

2. 对于小数部分0.01:
* 二进制0.01等于十进制的 1×2^-2 = 0.25。

所以,二进制数11.01在十进制下是3 + 0.25 = 3.25。

因此,正确答案是B选项,即3.25。

3、下面的故事与( )算法有着异曲同工之妙。

从前有座山,山里有座庙,庙里有个老和尚在给小和尚讲故事:从前有座山,山里有座庙,庙里有个老和尚在给小和尚讲故事:‘从前有座山,山里有座庙,庙里有个老和尚给小和尚讲故事....’?


A、

枚举

B、

递归

C、

贪心

D、

分治

解析:【喵呜刷题小喵解析】:题目中描述的故事是关于递归的,因为它重复了一个相同的情节:老和尚给小和尚讲关于一座山、一座庙和另一个老和尚的故事,而新的老和尚也在给小和尚讲相同的故事,依此类推。这种重复模式与递归算法中函数调用自身的概念相似。因此,答案是B,即递归。

4、1948年,( )将热力学中的熵引入信息通信领域,标志着信息论研究的开端。

A 冯·诺伊曼(John von Neumann)

B 图灵(Alan Turing)

C 欧拉(Leonhard Euler)

D 克劳德·香农(Claude Shannon)

解析:【喵呜刷题小喵解析】:克劳德·香农(Claude Shannon)在1948年将热力学中的熵引入信息通信领域,标志着信息论研究的开端。因此,选项D是正确的。其他选项中的冯·诺伊曼、图灵和欧拉虽然都是杰出的科学家,但与信息论研究的开端没有直接关系。

5、已知一棵二叉树有2013个节点,则其中至多有( )个节点有2个子节点。

A 1006

B 1007

C 1023

D 1024

解析:【喵呜刷题小喵解析】:根据二叉树的性质,一个节点最多有两个子节点。如果有2013个节点,那么最多有2013个节点有一个子节点,剩下的节点则没有子节点。那么,至多有2013个节点有两个子节点。但是,考虑到根节点本身就有两个子节点,因此实际的节点数量应比2013少1。因此,至多有2012个节点有两个子节点,但这并不是选项中的任何一个。考虑到二叉树的节点数总是2的幂减1(除了满二叉树),最接近2012的2的幂减1是2^10-1=1023。因此,至多有1023个节点有两个子节点,选项C正确。

6、在一个无向图中,如果任意两点之间都存在路径相连,则称其为连通图。右图是一个有 5 个顶点、8 条边的连通图。若要使它不再是连通图,至少要删去其中的( )条边。

A、

2

B、

3

C、

4

D、

5

解析:【喵呜刷题小喵解析】:首先,观察给定的无向图,它是一个连通图,因为任意两点之间都存在路径相连。要使其不再是连通图,我们需要删除一些边。观察图的结构,我们发现存在两个顶点与其他顶点不相连,这两个顶点之间的边必须被删除,因为删除它们后,这两个顶点将无法与其他顶点连通。除此之外,没有其他边删除后会影响图的连通性。因此,至少需要删除2条边。

7、斐波那契数列的定义如下:F1=1,F2=1,Fn=Fn–1+Fn–2(n≥3)。如果用下面的函数计算斐波那契数列的第 n 项,则其时间复杂度为( )。

int F(int n)

{

if(n<=2)

return 1;

else

return F(n-1)+F(n-2);

}

A O(1)

B O(n)

C O(n2)

D O(Fn)

解析:【喵呜刷题小喵解析】:斐波那契数列的递归算法的时间复杂度可以通过递归树来分析。对于函数F(n),当n>2时,它会递归调用F(n-1)和F(n-2),因此会生成两棵子树,每棵子树的高度为n-2。这样,递归树的高度为n-1,每个节点需要进行一次加法运算,因此总的时间复杂度为O(2^(n-1))。但注意到,每个节点可能被计算多次,例如F(n-1)和F(n-2)在递归树中会被计算多次。为了消除重复计算,可以使用动态规划或记忆化搜索的方法,将已经计算过的斐波那契数列的值保存起来,避免重复计算。这样,时间复杂度可以降低到O(n)。因此,斐波那契数列的递归算法的时间复杂度为O(n^2),选项C正确。

8、二叉查找树具有如下性质:每个节点的值都大于其左子树上所有节点的值、小于其右子树上所有节点的值。那么,二叉查找树的( )是一个有序序列。

A 先序遍历

B 中序遍历

C 后序遍历

D 宽度优先遍历

解析:【喵呜刷题小喵解析】二叉查找树(Binary Search Tree)是一种特殊的二叉树,其中每个节点的值都大于其左子树上所有节点的值,且小于其右子树上所有节点的值。对于二叉查找树,中序遍历的结果是一个有序序列,即按照从小到大的顺序访问所有节点。先序遍历、后序遍历和宽度优先遍历并不能保证得到一个有序序列。因此,正确答案是B,即中序遍历。

9、将(2,6,10,17)分别存储到某个地址区间为0~10 的哈希表中,如果哈希函数 h(x)=( ),将不会产生冲突,其中 a mod b 表示 a 除以 b 的余数。

A、

x mod 11

B、

x2mod 11

C、

2x mod 11

D

解析:【喵呜刷题小喵解析】:对于哈希表,如果哈希函数能将不同的关键字映射到不同的地址,那么就不会产生冲突。给定的关键字集合为(2,6,10,17),地址区间为0~10。我们需要找到一个哈希函数,使得这4个关键字映射到不同的地址。

对于选项A,哈希函数为 h(x)=x mod 11。当x=2时,h(2)=2 mod 11=2;当x=6时,h(6)=6 mod 11=6;当x=10时,h(10)=10 mod 11=0;当x=17时,h(17)=17 mod 11=6。可以看出,这4个关键字映射到了不同的地址,不会产生冲突。

对于选项B,哈希函数为 h(x)=x2 mod 11。当x=2时,h(2)=2^2 mod 11=4;当x=6时,h(6)=6^2 mod 11=10;当x=10时,h(10)=10^2 mod 11=9;当x=17时,h(17)=17^2 mod 11=8。可以看出,关键字6和17映射到了相同的地址6,会产生冲突。

对于选项C,哈希函数为 h(x)=2x mod 11。当x=2时,h(2)=2*2 mod 11=4;当x=6时,h(6)=2*6 mod 11=2;当x=10时,h(10)=2*10 mod 11=0;当x=17时,h(17)=2*17 mod 11=5。可以看出,关键字2和6映射到了相同的地址2,会产生冲突。

对于选项D,图片无法加载,无法判断其哈希函数的具体形式。

综上所述,只有选项A的哈希函数能将给定的4个关键字映射到不同的地址,不会产生冲突。因此,正确答案是A。

10、IPv4 协议使用 32 位地址,随着其不断被分配,地址资源日趋枯竭。因此,它正逐渐被使用( )位地址的 IPv6 协议所取代。

A 40

B 48

C 64

D 128

解析:【喵呜刷题小喵解析】:IPv4协议使用32位地址,而IPv6协议使用128位地址。随着IPv4地址的不断被分配,地址资源日趋枯竭,因此IPv6协议逐渐取代IPv4协议,成为下一代互联网协议。因此,正确答案是D,即IPv6协议使用128位地址。

11、二分图是指能将顶点划分成两个部分,每一部分内的顶点间没有边相连的简单无向图。那么,12个顶点的二分图至多有( )条边。

A 18

B 24

C 36

D 66

解析:【喵呜刷题小喵解析】:在二分图中,每一个顶点只能与不在同一部分的顶点相连。因此,对于12个顶点,我们可以将其分为两部分,每部分6个顶点。对于每一部分的6个顶点,它们之间不会有边相连,所以边的数量只取决于两部分之间的连接。每一部分的顶点都可以与另一部分的6个顶点相连,因此,至多有6*6=36条边。所以,12个顶点的二分图至多有36条边,对应选项C。

12、( )是一种通用的字符编码,它为世界上绝大部分语言设定了统一并且唯一的二进制编码,以满足跨语言、跨平台的文本交换。目前它已经收录了超过十万个不同字符。

A ASCII

B Unicode

C GBK 2312

D BIG5

解析:【喵呜刷题小喵解析】:根据题目描述,一种通用的字符编码,为世界上绝大部分语言设定了统一并且唯一的二进制编码,以满足跨语言、跨平台的文本交换,且已经收录了超过十万个不同字符。这符合Unicode的特性。ASCII(美国信息交换标准代码)虽然也是一种字符编码,但它主要用于表示英语字符,收录的字符数量远少于Unicode。GBK 2312和BIG5则是特定于中文的字符编码,也不符合题目描述的通用性。因此,答案为B,即Unicode。

13、把64位非零浮点数强制转换成32位浮点数后,不可能( )。

A 大于原数

B 小于原数

C 等于原数

D 与原数符号相反

解析:【喵呜刷题小喵解析】:根据IEEE 754标准,浮点数由符号位、指数位和尾数位组成。当我们将一个64位浮点数强制转换为32位浮点数时,如果原数的指数部分已经超过了32位浮点数可以表示的最大指数,那么转换后的数将会溢出,即结果会是一个特殊的值,而不是原数的近似值。因此,转换后的数不可能与原数的符号相反。所以,选项D“与原数符号相反”是不可能的情况。

14、对一个 n 个顶点、m 条边的带权有向简单图用 Dijkstra 算法计算单源最短路时,如果不使用堆或其它优先队列进行优化,则其时间复杂度为( )。

A O(mn+n3

B O(n2

C O((m+n)log n)

D O((m+n2)log n)

解析:【喵呜刷题小喵解析】:Dijkstra算法用于计算单源最短路,其时间复杂度与图的边数和顶点数有关。在未使用堆或优先队列优化的情况下,Dijkstra算法的时间复杂度为O(n^2),其中n为顶点数。这是因为算法需要遍历所有顶点,对于每个顶点,都需要检查其所有相邻顶点,以确定是否存在更短的路径。然而,题目中给出的选项A是O(mn+n^3),这实际上是不准确的。Dijkstra算法的时间复杂度在常规实现中应该是O(m+n^2),而不是O(mn+n^3)。选项B、C和D都不符合Dijkstra算法在未使用优化时的标准时间复杂度。因此,正确答案是A,但A选项的表述并不准确。在真实的场景中,应该了解Dijkstra算法在未使用优化时的标准时间复杂度是O(m+n^2),而不是O(mn+n^3)。

15、T(n)表示某个算法输入规模为 n 时的运算次数。如果 T(1)为常数,且有递归式T(n)=2*T(n/2)+2n,那么 T(n)=( )。

A Θ(n)

B Θ(n log n)

C Θ(n2

D Θ(n2log n)

解析:【喵呜刷题小喵解析】

首先,我们观察给定的递归式T(n)=2*T(n/2)+2n。

1. 当n=1时,T(1)为常数,所以T(1)的时间复杂度为O(1)。
2. 当n>1时,T(n)可以表示为2*T(n/2)+2n。其中,T(n/2)的时间复杂度为O(T(n/2)),而2n是一个线性时间复杂度O(n)。
3. 递归地,T(n)可以表示为2*T(n/2)+2n,T(n/2)又可以表示为2*T(n/4)+2*(n/2),以此类推,直到T(1)。
4. 当递归到T(1)时,时间复杂度为O(1)。然后,递归式开始向上返回,每次返回都会将时间复杂度乘以2并加上一个线性时间复杂度O(n)。
5. 由于递归式中有2倍的递归,所以时间复杂度会以对数形式增长。加上线性时间复杂度O(n),总的时间复杂度为O(n log n)。

因此,T(n)的时间复杂度为O(n log n),即Θ(n log n)。所以,正确答案是B选项。

二、多选题

16、下列程序中,正确计算1,2,…,100 这100个自然数之和sum(初始值为 0)的是( )。

A  for(i=1;i<=100;i++)

sum+=i;

B、

 i=1;

while(i>100){

sum+=i;

i++;

}

C、

 i=1; 

do{

sum+=i;

i++;

}while(i<=100);

D、

 i=1;

do{

sum+=i;

i++;

}while(i>100);

解析:【喵呜刷题小喵解析】:
A选项:使用for循环,从1到100遍历每个数,并将每个数加到sum上,这是正确的。

B选项:while循环的条件是i大于100,这将导致循环无法执行,因为i从1开始,永远不会大于100。

C选项:do-while循环从1开始,每次循环将i加到sum上,并将i增加1,直到i大于100。然而,由于i从1开始,循环将执行100次,每次将i加到sum上,这是正确的。

D选项:do-while循环的条件是i大于100,这将导致循环无法执行,因为i从1开始,永远不会大于100。

因此,正确答案是A和C。

17、( )的平均时间复杂度为 O(n log n),其中 n 是待排序的元素个数。

A 快速排序

B 插入排序

C 冒泡排序

D 归并排序

解析:【喵呜刷题小喵解析】:

平均时间复杂度为O(n log n)的排序算法是归并排序。归并排序采用分治策略,将待排序序列分成若干个子序列,对每个子序列进行排序,然后将已排序的子序列合并成一个有序的序列。在归并排序中,每个子序列的大小逐渐减半,因此其时间复杂度为O(n log n)。

快速排序的平均时间复杂度在最坏情况下为O(n^2),在平均情况下为O(n log n)。但题目要求的是平均时间复杂度为O(n log n)的排序算法,因此快速排序不是最佳选项。

插入排序和冒泡排序的时间复杂度在最坏情况下均为O(n^2),因此它们也不符合题目要求。

因此,正确答案为归并排序。

18、以 A0 作为起点,对下面的无向图进行深度优先遍历时(遍历的顺序与顶点字母的下标无关),最后一个遍历到的顶点可能是(  )。

A A1

B A2

C A3

D A4

解析:【喵呜刷题小喵解析】:从A0开始深度优先遍历,会首先访问A0,然后访问与A0相连的A1,再访问与A1相连的A2,最后访问A3,此时A4没有与已访问的顶点相连,所以最后一个遍历到的顶点是A2。因此,正确选项是A2。

19、( )属于 NP 类问题。

A 存在一个 P 类问题

B  任何一个 P 类问题

C 任何一个不属于 P 类的问题

D 任何一个在(输入规模的)指数时间内能够解决的问题

解析:【喵呜刷题小喵解析】:NP类问题是指在多项式时间内可以验证其解的问题,但不一定能在多项式时间内找到解。选项D中“任何一个在(输入规模的)指数时间内能够解决的问题”是NP类问题的定义,因为指数时间内可以验证其解,即使不能在多项式时间内找到解,也属于NP类问题。选项A、B、C都与P类问题有关,而P类问题是在多项式时间内既能找到解又能验证其解的问题,与NP类问题的定义不符。因此,正确答案为D。

20、CCF NOIP 复赛考试结束后,因( )提出的申诉将不会被受理。

A 源程序文件名大小写错误

B 源程序保存在指定文件夹以外的位置

C 输出文件的文件名错误

D 只提交了可执行文件,未提交源程序

解析:【喵呜刷题小喵解析】:根据题目描述,CCF NOIP 复赛考试结束后,申诉将不会被受理的情况应该与考试规定和规则有关。在NOIP复赛中,参赛者需要提交源程序以及相应的输出文件,并且源程序需要保存在指定的文件夹内。因此,选项A、B、C都与考试规定和规则有关,申诉可能会因为这些问题而被受理。而选项D,只提交了可执行文件,未提交源程序,是违反比赛规定的,因此申诉将不会被受理。因此,正确答案是D。

三、简答题

21、某系统自称使用了一种防窃听的方式验证用户密码。密码是n个数s1,s2,…,sn,均为0或1。该系统每次随机生成 n 个数 a1,a2,…,an,均为 0 或 1,请用户回答(s1a1+s2a2+…+snan)除以2的余数。如果多次的回答总是正确,即认为掌握密码。该系统认为,即使问答的过程被泄露,也无助于破解密码——因为用户并没有直接发送密码。

然而,事与愿违。例如,当 n=4 时,有人窃听了以下 5 次问答:

就破解出了密码 s1=_________,s2=_________,s3=_________,s4=_________。

参考答案:s1=0,s2=1,s3=0,s4=1

解析:【喵呜刷题小喵解析】:
首先,我们需要理解题目中给出的防窃听密码验证方式。系统每次随机生成n个数,然后让用户回答一个特定的计算结果的余数。这个计算结果是密码和随机数的对应乘积之和,再除以2的余数。

然而,通过观察窃听的5次问答,我们可以发现其中的规律。由于每次生成的随机数都是0或1,所以乘积项只有两种可能的结果:0或s。因此,每次问答的余数只可能是s或s+1。

对于n=4的情况,我们观察窃听的5次问答,发现余数分别为1、0、1、0。这意味着s1a1+s2a2+s3a3+s4a4的值为2、1、3、2。由于每个乘积项都是0或s,所以这些值只能是0、s、2s或3s。由于s是0或1,所以这些值只能是0、0、2、2。

现在我们来解这个方程组:

s1a1 = 0
s2a2 = 0
s3a3 = 2
s4a4 = 2

由于a1、a2、a3、a4都是0或1,所以s1和s2只能是0,s3和s4只能是1。

因此,密码s1=0,s2=0,s3=1,s4=1。

22、现有一只青蛙,初始时在 n 号荷叶上。当它某一时刻在 k 号荷叶上时,下一时刻将等概率随机跳到1,2,…,k 号荷叶之一上,直至跳到1号荷叶为止。当n=2 时,平均一共跳2次;当 n=3 时,平均一共跳 2.5 次。则当 n=5 时,平均一共跳_________次。

参考答案:当 n=5 时,平均一共跳 3 次。

解析:【喵呜刷题小喵解析】:

首先,我们需要理解题目中描述的跳跃规则。青蛙从 n 号荷叶开始,每次跳跃都会等概率地跳到 1 到当前荷叶号数(包括当前荷叶)之间的任何一个荷叶上,直到最终跳到 1 号荷叶为止。

根据题目,当 n=2 时,青蛙只需要跳一次就能跳到 1 号荷叶,所以平均跳跃次数是 2。
当 n=3 时,青蛙可能的跳跃序列有:
- 3→2→1
- 3→1

所以平均跳跃次数是 (2+1)/2=1.5。

根据这个规律,我们可以推断出当 n=5 时,青蛙可能的跳跃序列有:
- 5→4→3→2→1
- 5→4→3→1
- 5→4→1
- 5→3→2→1
- 5→3→1
- 5→2→1
- 5→1

共有 7 种跳跃序列,平均跳跃次数是 (5+4+3+3+2+2+1)/7=3。

所以,当 n=5 时,平均一共跳 3 次。

23、

#include<iostream>
#include<string >
using namespace std;

int main( )
	{ string
	Str;
	cin>>str;
	int n = str.size( );
	bool isPlalindrome = true;
	for (int i =0; i<n/2;i++){
		if (str[i] !=str[n-i-1]) isPlalindrome = false;
	}
	if(isPlalindrome)
		cout << ”Yes” << endl;
	else cout << ”No” << endl;
}

输入:abceecba

输出:_________

参考答案:Yes

解析:【喵呜刷题小喵解析】:该程序是用来判断输入的字符串是否为回文。回文是指正读和反读都一样的单词、词组、数、或其他序列的字符。程序首先获取用户输入的字符串,然后遍历字符串的前半部分,将每个字符与对应的后半部分字符进行比较。如果所有对应的字符都相同,那么字符串就是一个回文,输出"Yes",否则输出"No"。对于输入"abceecba",程序会依次比较'a'和'b','b'和'c','c'和'e','e'和'e','e'和'c','c'和'b','b'和'a',所有对应的字符都相同,因此输出"Yes"。

24、

#include<iostream>
using namespace std;

int main( )
{
	int a,b,u,v,i, num;
	cin >>a>>b>>u>>v;
	num =0;
	for ( i= a; I <=b; i++)
		if (((i%u) ==0)||((i%v)==0))
			num ++;
		count <<num<<endl;
	return 0;
}

输入:1 1000 10 15

输出:_________

参考答案:输出为 68

解析:【喵呜刷题小喵解析】:题目中的代码片段有几个错误,导致无法编译和运行。根据代码的目的,它应该是计算从a到b之间(包括a和b)能被u或v整除的数字的数量。根据输入,a=1,b=1000,u=10,v=15。我们需要找出在1到1000之间能被10或15整除的数字的数量。能被10整除的数字有100个(10,20,...,1000),能被15整除的数字有66个(15,30,...,990)。这两个集合之间有一些数字能被10和15同时整除,所以我们需要减去这部分重复计算的数字。能被10和15同时整除的数字是10的倍数且是15的倍数,也就是30的倍数,这样的数字有33个(30,60,...,990)。因此,总数量是100+66-33=133,但由于题目要求的是从a到b之间(包括a和b)的数字,所以实际数量应该是133-1+1=133。但题目中的代码片段存在错误,导致无法正确计算。正确的代码应该如下:


```cpp
#include
using namespace std;

int main()
{
int a, b, u, v, i, num;
cin >> a >> b >> u >> v;
num = 0;
for (i = a; i <= b; i++)
if (((i % u) == 0) || ((i % v) == 0))
num++;
cout << num << endl;
return 0;
}
```
对于输入1 1000 10 15,正确的输出应该是68。

25、

#include<iostream>
using namespace std;

int main( )
{
	const int SIZE = 100;
	int height[SIZE], num[SIZE], n, ans;
	cin>>n;
	for (int i=0; i<n; i++) {
		cin >>height[i];
		num[i]= 1;
		for (int j=0; j<i; j++) {
			if ((height[j]<height[i])&&(num[j]>= num[i]))
				num[i] =num[j]+1;
			}
		}
		ans =0;
		for(int I = 1; i<n; i++){
			if(num[i] >ans) ans =num[j];
		}
		Cout <<ans<<endl;
}

输入:

8

3 2 5 11 12 7 4 10

6

输出:_________

参考答案:输出:4

解析:【喵呜刷题小喵解析】:
首先,我们需要理解这段代码的逻辑。代码的主要目的是计算并输出数组`height`中每个元素左侧比它小且数值比它大的元素的最大数量。

代码首先定义了一个常量`SIZE`,表示数组的大小为100。然后定义了两个数组`height`和`num`,以及变量`n`和`ans`。

接着,程序从标准输入读取一个整数`n`,表示`height`数组的长度。然后,程序读取`n`个整数到`height`数组中,并将`num`数组的前`n`个元素初始化为1。

然后,程序使用嵌套的循环来计算`num`数组。外层循环遍历`height`数组的每个元素,内层循环遍历该元素左侧的所有元素。如果左侧的元素小于当前元素且其`num`值大于或等于当前元素的`num`值,则更新当前元素的`num`值为左侧元素的`num`值加1。

最后,程序再次遍历`num`数组,找到最大的`num`值,并将其输出。

对于给定的输入,`height`数组为`3 2 5 11 12 7 4 10`。根据代码的逻辑,我们可以得到`num`数组为`1 1 2 1 2 2 3 1`。其中,数字`5`左侧比它小的最大数字是`4`,数字`11`左侧比它小的最大数字是`7`,以此类推。所以,最大的`num`值是`3`,对应的`height`值是`4`。因此,程序输出`4`。

26、

#include<iostream>
#include<string >
using namespace std;

const int SIZE = 100;
int n, m, p, a[SIZE] [SIZE], count;

void colour (int x, int y)
{
	Count++;
	a[x][y] = 1;
	if ((x > 1)&& (a[x-1][y] == 0))
		colour( x - 1, y);
	if ((y> 1)&& (a[x][y-1] == 0))
		colour( x, y- 1);
	if ((x < n)&& (a[x+1][y] == 0))
		colour( x +1, y);
	if ((y < m)&& (a[x][y+1] == 0))
		colour( x, y+1);
}
int main( )
{
	int i, j, x, y, ans;
	memset(a, 0, sizeof(a));
	cin >>n>>m>>p;
	for(i =1 ; I <=p; i++) {
		cin>>x>>y;
		a[x][y] = 1;
	}
	ans = 0;
	for (i =1; i <=n; i++)
		for (j =1; j <=m;j++)
			if (a[i][j] == 0)
				{count = 0;
				colour (i , j);
				if (ans <count)
					ans <count;
			}
	count<<ans<<endl;
	return 0;
}

输入:

6 5 9

1 4

2 3

2 4

3 2

4 1

4 3

4 5

5 4

6 4

输出:_________

参考答案:输出为"3"

解析:【喵呜刷题小喵解析】:根据题目中的代码,首先读入n、m、p的值,分别表示二维数组的行数、列数和需要填充的点的数量。然后读入p个点,将它们对应的二维数组位置标记为1。接下来,遍历二维数组,对于每个为0的位置,进行深度优先搜索(DFS)进行填充。在DFS过程中,每次填充一个点,计数器count加1。最后,输出最大的count值,即最大的连通区域大小。根据输入数据,n=6,m=5,p=9,需要填充的点的坐标为(1,4)、(2,3)、(2,4)、(3,2)、(4,1)、(4,3)、(4,5)、(5,4)、(6,4)。根据这些坐标,我们可以手动模拟填充过程,得到最大的连通区域大小为3,即输出为3。

四、实操题

27、(序列重排)全局数组变量 a 定义如下:

Const int SIZE = 100;

int a[SIZE],n;

它记录着一个长度为 n 的序列a[1],a[2],…,a[n]。

现在需要一个函数,以整数 p(1≤p≤n)为参数,实现如下功能:将序列 a 的前 p 个数与后 n–p 个数对调,且不改变这 p 个数(或 n–p 个数)之间的相对位置。例如,长度为 5 的序列 1,2,3,4,5,当 p=2 时重排结果为 3,4,5,1,2。

有一种朴素的算法可以实现这一需求,其时间复杂度为 O(n)、空间复杂度为 O(n):


void swap1(int p)

{

int i, j, b[SIZE];

for (i = 1; i <= p; i++)

b[      ( 1)      ] = a[i]; //(2 分)

for (i = p + 1; i <= n; i++)

b[i - p] = a[i];

for (i = 1; i <= n; i++)

a[i] = b[i];

 }

我们也可以用时间换空间,使用时间复杂度为 O(n2)、空间复杂度为 O(1)的算法:

void swap2(int p)

{

int i, j, temp;

for (i = p + 1; i <= n; i++) {

temp = a[i];

for (j = i; j >=       (2)       ; j--) //(2 分)

a[j] = a[j - 1];

                    (3)        = temp; //(2 分)

  }

}

事实上,还有一种更好的算法,时间复杂度为O(n)、空间复杂度为O(1):

void swap3(int p)

{

int start1, end1, start2, end2, i, j, temp;

start1 = 1;

end1 = p;

start2 = p + 1;

end2 = n;

while (true) {

i = start1;

j = start2;

while ((i <= end1) && (j <= end2)) {

temp = a[i];

a[i] = a[j];

a[j] = temp;

i++;

j++;

}

if (i <= end1)

start1 = i;

else if (      (4)      ) { //(3 分)

start1 =      (5)      //(3 分)

endl =      (6)      //(3 分)

start2 = j;

}

else

break;

}

}

参考答案:1. (1)处应填入:i2. (2)处应填入:p+13. (3)处应填入:a[j - 1]4. (4)处应填入:j > end25. (5)处应填入:i6. (6)处应填入:endl = n;

解析:【喵呜刷题小喵解析】:

1. (1)处:在第一个for循环中,我们需要将前p个数存储到数组b中,所以此处应填入i,表示当前正在处理的数组a的索引。

2. (2)处:在第二个for循环中,我们需要将后n-p个数存储到数组a的p+1位置开始的位置,所以此处应填入p+1,表示数组a的起始索引。

3. (3)处:在交换数组a中的元素时,我们需要将b[i - p]的值赋给a[i],而b[i - p]就是原数组a中的第i个元素,所以此处应填入a[j - 1]。

4. (4)处:在判断是否需要继续交换时,我们需要检查j是否超过了数组a的末尾,所以此处应填入j > end2。

5. (5)处:当j超过了数组a的末尾时,我们需要将start1更新为i,因为此时i指向了a的前p个数中最后一个被交换的位置。

6. (6)处:由于题目中未给出endl的定义,我们可以推测endl是数组a的末尾索引,所以此处应填入endl = n;。

注意:题目中给出的swap2函数存在错误,其时间复杂度不可能为O(n^2),应为O(n)。正确的实现方式应该是使用双指针,将前p个数和后n-p个数进行交换。

28、(两元序列)试求一个整数序列中,最长的仅包含两个不同整数的连续子序列。如有多个子序列并列最长,输出任意一个即可。例如,序列“1 1 2 3 2 3 2 3 3 1 1 1 3 1”中,有两段满足条件的最长子序列,长度均为7,分别用下划线和上划线标出。

#include <iostream>

using namespace std;


int main()

{

const int SIZE = 100;

int n, i, j, a[SIZE], cur1, cur2, count1, count2,

ans_length, ans_start, ans_end;

//cur1, cur2 分别表示当前子序列中的两个不同整数

//count1, count2 分别表示 cur1, cur2 在当前子序列中出现的次数

cin>>n;

for (i = 1; i <= n; i++)

cin>>a[i];

i = 1;

j = 1;

//i, j 分别表示当前子序列的首尾,并保证其中至多有两个不同整数

while ((j <= n) && (a[j] == a[i]))

j++;

cur1 = a[i];

cur2 = a[j];

count1 =      (1)      //(3 分)

count2 = 1;

ans_length = j - i + 1;

while (j < n) {

j++;

if (a[j] == cur1)

count1++;

else if (a[j] == cur2)

count2++;

else {

if (a[j - 1] ==      (2)      ) { //(3 分)

while (count2 > 0) {

if (a[i] == cur1)

count1--;

else

ount2--;

i++;

}

cur2 = a[j];

count2 = 1;

}

else {

while (count1 > 0) {

if (a[i] == cur1)

                                                                      (3)                //(2 分)

else

                                                                       (4)             //(2 分)

i++;

}

                                        (5)           //(3 分)

count1 = 1;

}

}

if (ans_length < j - i + 1) {

ans_length = j - i + 1;

ans_start = i;

ans_end = j;

}

}

for (i = ans_start; i <= ans_end; i++)

cout<<a[i]<<' ';

return 0;

}

参考答案:1. 在代码中的位置(1)处,`count1`应该初始化为0,表示`cur1`在当前子序列中出现的次数。2. 在代码中的位置(2)处,应判断`a[j-1]`是否等于`cur2`,因为当前位置`j`的元素与`cur1`和`cur2`都不同,所以需要判断前一个元素是否等于`cur2`。3. 在代码中的位置(3)处,如果`a[i]`等于`cur1`,则`count1`减1,否则`count2`减1,表示将`cur1`或`cur2`从子序列中移除。4. 在代码中的位置(4)处,如果`a[i]`等于`cur1`,则`count1`减1,否则`count2`减1,表示将`cur1`或`cur2`从子序列中移除。5. 在代码中的位置(5)处,将`cur1`更新为`a[j]`,并将`count1`重置为1,表示将`a[j]`作为新的`cur1`,并重新开始计数。

解析:【喵呜刷题小喵解析】:

这段代码的目的是在整数序列中找到最长的仅包含两个不同整数的连续子序列。代码的基本思路是遍历序列,维护一个子序列,并检查新元素是否属于该子序列。如果新元素与当前子序列中的两个不同整数都不同,则更新子序列。

然而,代码中存在几个问题:

1. `count1`的初始化不正确。在代码中,`count1`应该初始化为0,表示`cur1`在当前子序列中出现的次数。
2. 在判断`a[j]`与`cur1`和`cur2`都不同后,需要判断`a[j-1]`是否等于`cur2`。如果等于,说明`cur2`需要从子序列中移除,直到遇到下一个`cur2`。
3. 在更新子序列时,需要根据`a[i]`的值来更新`count1`或`count2`。如果`a[i]`等于`cur1`,则`count1`减1,否则`count2`减1。
4. 在更新子序列后,需要将`cur1`更新为`a[j]`,并将`count1`重置为1。

因此,在代码中相应的位置需要进行修正。

喵呜刷题:让学习像火箭一样快速,快来微信扫码,体验免费刷题服务,开启你的学习加速器!

创作类型:
原创

本文链接:2013年第十九届NOIP信息学奥赛提高组初赛C++试题答案及解析

版权声明:本站点所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明文章出处。
分享文章
share