Bootstrap

2024年12月GESPC++四级真题解析

一、单选题(每题2分,共30分)

题目123456789101112131415
答案

A

B

B

B

D

D

D

D

A

C

B

B

A

C

A

1.下面的语句中,( )正确定义了一个计算浮点数x的平方(x2=x×x)的函数,并成功调用该函数。

A. 
float square(float x) {

return x * x;

}

float area = square(2);

B.
square(float x) {

return x * x;

}

float area = square(2);

C.
void square(float x) {

return x * x;

}

area = square(2.0);

D.
void square(float x) {

x * x;

return;

}

area = square(2);

【答案】A

【考纲知识点】函数的定义和调用。

【解析】
选项 A:

float square(float x) { return x * x; } 正确地定义了一个返回值为 float 类型的函数 square ,用于计算输入浮点数 x 的平方。

float area = square(2); 正确地调用了该函数,并将返回值赋给了 area 变量。

选项 B:

square(float x) { return x * x; } 函数定义缺少返回值类型,这是不正确的语法。

选项 C:

void square(float x) 定义的函数没有返回值,而函数内部却有 return x * x; ,这是错误的,void 类型的函数不能有返回值。

选项 D:

同选项 C,void 类型的函数不能返回值,而且函数体中 x * x; 没有实际作用。

综上所述,正确的是选项 A 。

2.下面代码的描述中,正确的是( )。
void n_chars(char c, int n) {

while (n-- > 0)

cout << c;

}

char my_char = 'w';

int times = 5;

n_chars(my_char, times);



A. 代码执行结束后, times 的值为0

B. n 是形参, times 是实参

C. n 是实参, times 是形参

D. 代码最后一行换成 n_chars(times, my_char); 也可以

【答案】B

【考纲知识点】函数参数传递。

【解析】
选项 A:在函数 n_chars 中,虽然使用了 n-- ,但这并不会改变实参 times 的值,所以代码执行结束后,times 的值仍为 5,选项 A 错误。

选项 B:在函数 n_chars(char c, int n) 中,c 和 n 是形参,调用函数 n_chars(my_char, times) 时,my_char 和 times 是实参,所以 n 是形参,times 是实参,选项 B 正确。

选项 C:说法正好与选项 B 相反,是错误的。

选项 D:函数 n_chars 的定义是第一个参数为字符,第二个参数为整数,所以 n_chars(times, my_char); 这种调用方式参数类型不匹配,是错误的,选项 D 错误。

综上所述,正确的是选项 B 。

3. 给定以下代码,
void func(int& x) {

x = x * 2;

}

int a = 5;

func(a);
执行上述代码后,变量 a 的值为( )。

A. 5

B. 10

C. 15

D. 20

【答案】B

【考纲知识点】引用作为函数参数。

【解析】在 func 函数中,参数 x 是对实参的引用。

当调用 func(a) 时,x 实际上就是 a 的一个别名,对 x 的操作就是对 a 的操作。

在函数内部 x = x * 2 ,即 x = a * 2 = 5 * 2 = 10 ,所以 a 的值也变为 10 。

答案选择 B 选项。

4.运行下面代码,屏幕上输出是( )。
double* p_arr = new double [3];

p_arr[0] = 0.2;

p_arr[1] = 0.5;

p_arr[2] = 0.8;

p_arr += 1;

cout << p_arr[0] << endl;

p_arr -= 1;

delete p_arr;

A. 0.2

B. 0.5

C. 1.2

D. 1.5

【答案】B

【考纲知识点】指针运算和动态内存分配。

【解析】
首先,通过 new 操作符分配了一个包含 3 个 double 类型元素的动态数组,并将指针 p_arr 指向这个数组的首地址。

然后给数组的元素赋值,p_arr[0] = 0.2,p_arr[1] = 0.5,p_arr[2] = 0.8。

接着执行 p_arr += 1,这使得指针 p_arr 向后移动了一个位置,此时它指向了原来数组的第二个元素。

所以当输出 p_arr[0] 时,实际上输出的是原来数组的第二个元素,即 0.5。

之后又执行 p_arr -= 1 将指针恢复到原来的位置。

最后使用 delete 释放动态分配的内存。

因此,屏幕上输出的是 0.5,选择 B 选项。

5.运行下面代码片段后, x 和 *p 的结果分别是( )。
int x = 20;

int* p = &x;

*p = *p + 2;

A. 20 20

B. 20 22

C. 22 20

D. 22 22

【答案】D

【考纲知识点】指针操作和变量的值。

【解析】
首先,定义了一个整型变量 x 并初始化为 20 ,同时定义了一个指向 x 的指针 p 。

然后,通过 *p = *p + 2 ,因为 *p 就是 x ,所以这相当于对 x 进行了加 2 的操作,x 的值变为 22 。

此时,*p 指向的仍然是 x ,所以 *p 的值也是 22 。

所以,x 的值是 22 ,*p 的值也是 22 ,选择 D 选项。

6.下面的描述中,( )不能正确定义一个名为 Student 的结构体以及一个包含20个元素的结构数组。

A. 
struct Student {

string name;

int age;

float score;

};

struct Student students[20];

B.
struct Student {

string name;

int age;

float score;

};

Student students[20];

C.
struct Student {

string name;

int age;

float score;

};

Student* students = new Student[20];

D.
struct Student {

string name;

int age;

float score;

};

Student students = new Student[20];

【答案】D

【考纲知识点】结构体的定义和数组的声明。

【解析】
选项 A:先定义了 Student 结构体,然后声明了一个包含 20 个 Student 结构体元素的数组 students ,这种方式是正确的。

选项 B:在结构体定义之后,直接使用 Student 类型声明数组 students[20] ,这也是合法的。

选项 C:使用动态分配内存的方式创建了一个包含 20 个 Student 结构体的数组,并将指针 students 指向它,是正确的操作。

选项 D:Student students = new Student[20]; 这种写法是错误的,不能直接用 = 来将动态分配的数组赋值给一个结构体变量。

综上所述,不能正确定义的是选项 D 。

7.假定整型是32位,对一个 行 列的二维整数数组 array ,假设数组第一个元素在内存中的地址为0x7ffee4065820 ,则第2行第2个元素的地址 &array[1][1] 为( )。
int array[2][3] = {

{0, 1, 2},

{3, 4, 5}

};

A. 0x7ffee4065824

B. 0x7ffee4065828

C. 0x7ffee406582c

D. 0x7ffee4065830

【答案】D

【考纲知识点】二维数组在内存中的存储方式。

【解析】
在 32 位的整型环境中,一个整数占用 4 个字节。

二维数组 array[2][3] 在内存中是按行优先存储的,即先存储第一行的元素,再存储第二行的元素。

第一行有 3 个整数,共占用 3×4 = 12 个字节。

所以第二行第一个元素的地址相对于数组起始地址偏移了 12 个字节。

array[1][1] 是第二行的第二个元素,相对于第二行第一个元素又偏移了 4 个字节。

所以 &array[1][1] 相对于数组起始地址 0x7ffee4065820 偏移了 12 + 4 = 16 个字节。

十六进制下 16 转化为 0x10 ,起始地址加上 0x10 得到 0x7ffee4065820 + 0x10 = 0x7ffee4065830 。

所以选择 D 选项。

8.下面( )正确定义二维数组。
A. int a[3][];

B. int a[][];

C. int a[][4];

D. int a[][2] = {{1,2},{1,2},{3,4}};

【答案】D

【考纲知识点】二维数组的定义。

【解析】
选项 A:int a[3][] 这种定义方式是错误的,在定义二维数组时,除了第一维可以省略,其他维度都不能省略。

选项 B:int a[][] 这种定义方式不明确数组的大小,是错误的。

选项 C:int a[][4] 第一维的大小没有指定,也是错误的定义方式。

选项 D:int a[][2] = {{1, 2}, {1, 2}, {3, 4}}; 正确地定义了一个二维数组,第一维大小根据初始化的值自动推断为 3,第二维大小明确为 2,并且给出了合理的初始化值。

综上所述,正确的是选项 D。

9.下面代码采用递推算法来计算斐波那契数列f(n)=f(n-1)+f(n-2) ,则横线上应填写( )。
int fib(int n) {

if (n == 0 || n == 1)

return n;

int f1 = 0;

int f2 = 1;

int result = 0;

for (int i = 2; i <= n; i++) {

________________________________ // 在此处填入代码

}

return result;

}

A. 
result = f1 + f2;

f1 = f2;

f2 = result;

B.
result += f1 + f2;

f1 = f2;

f2 = result;

C.
result += f1 + f2;

f2 = result;

f1 = f2;

D.
result = f1 + f2;

f2 = result;

f1 = f2;

【答案】A

【考纲知识点】递推算法。

【解析】
斐波那契数列的计算规则是当前项等于前两项之和。

在每次循环中:

首先计算出当前的斐波那契数 result = f1 + f2 。

然后更新前两项的值,将 f2 的值赋给 f1 ,将新计算出的 result 的值赋给 f2 ,为下一次循环做准备。

所以横线上应填写的代码是:
result = f1 + f2;

f1 = f2;

f2 = result;
选择选项 A 。

10.下面关于排序算法(冒泡排序、插入排序和选择排序)的描述中,不正确的是( )。

A. 冒泡排序基于元素交换实现,需借助临时变量,共涉及3个单元操作;而插入排序基于元素赋值实现,仅需个单元操作。因此冒泡排序的计算开销通常比插入排序更高。

B. 选择排序在任何情况下的时间复杂度都为O(n2) 。

C. 冒泡排序在任何情况下的时间复杂度都为 O(n2)

D. 如果给定数据部分有序,插入排序通常比选择排序效率更高。

【答案】C

【考纲知识点】常见排序算法的特点和时间复杂度。

【解析】
选项 A:冒泡排序通过不断交换相邻元素来排序,涉及比较、交换等操作,计算开销相对较大;插入排序通过逐个元素的赋值插入来排序,操作相对简单,所以冒泡排序通常比插入排序计算开销高,该描述正确。

选项 B:选择排序无论数据初始状态如何,其比较和交换的次数都是固定的,时间复杂度始终为 O (n²),该描述正确。

选项 C:冒泡排序在最好情况下(即数组已经有序),时间复杂度为 O (n),并非任何情况下都是 O (n²),该描述不正确。

选项 D:当给定数据部分有序时,插入排序利用了这种有序性,效率会更高,而选择排序不会因为数据的部分有序性而有明显效率提升,该描述正确。

综上所述,不正确的是选项 C。

11. 冒泡排序的第一轮操作是从左到右遍历数组,通过两两比较相邻元素,将当前最大的元素移动到末尾。给定数组 arr[]={4, 1, 3, 1, 5, 2} ,执行第一轮冒泡排序后数组 arr 中的内容为( )。

A. 1, 4, 3, 1, 5, 2

B. 1, 3, 1, 4, 2, 5

C. 1, 4, 3, 1, 2, 5

D. 4, 1, 3, 1, 5, 2

【答案】B

【考纲知识点】冒泡排序的原理和操作。

【解析】
冒泡排序第一轮,从左到右两两比较相邻元素,如果左边的元素大于右边的元素,就交换它们。

对于数组 arr[] = {4, 1, 3, 1, 5, 2} ,第一次比较 4 和 1,交换得到 {1, 4, 3, 1, 5, 2} ;第二次比较 4 和 3,交换得到 {1, 3, 4, 1, 5, 2} ;第三次比较 4 和 1,交换得到 {1, 3, 1, 4, 5, 2} ;第四次比较 4 和 5,不交换;第五次比较 5 和 2,交换得到 {1, 3, 1, 4, 2, 5} 。

所以第一轮冒泡排序后数组 arr 中的内容为 {1, 3, 1, 4, 2, 5} ,选择选项 B 。

12.给定如下代码,其时间复杂度为( )。
int cellRecur(int n) {

if (n == 1)

return 1;

return cellRecur(n - 1) + cellRecur(n - 1) + 1;

}

A.O(n2)

B.O(2n)

C.O(1)

D.O(n)

【答案】B

【考纲知识点】递归算法的时间复杂度分析。

【解析】
在这个递归函数 cellRecur 中,每次递归都会产生两个新的递归调用(cellRecur(n - 1) 两次),直到 n == 1 时终止。

这种递归的增长速度类似于二叉树的节点数量,其时间复杂度可以通过递归树来分析,大致为 O(2^n) 。

所以答案选择 B 。

13.下面代码实现了插入排序函数,则横线上应填写( )。
void insertion_sort(vector<int> &nums) {

for (int i = 1; i < nums.size(); i++) {

________________________________ { // 在此处填入代码

while (j >= 0 && nums[j] > base)

nums[j + 1] = nums[j];

j--;

}

nums[j + 1] = base;

}

}
A. int base = nums[i], j = i - 1;

B. int base = nums[i], j = i;

C. int base = nums[0], j = i - 1;

D. int base = nums[0], j = i;

【答案】A

【考纲知识点】插入排序算法的实现。

【解析】
在插入排序中,每次从待排序部分取出一个元素(即 nums[i] )作为基准值 base ,然后与已排序部分的元素从后往前进行比较和移动。

所以应该将 nums[i] 赋值给 base ,同时从 i - 1 位置开始与已排序部分进行比较,所以 j 初始值应该为 i - 1 。

因此,横线上应填写的代码是:

int base = nums[i], j = i - 1;

选择选项 A 。

14.下面哪种方式不能实现将字符串"Welcome to GESP!"输出重定向到文件 log.txt ( )。

A. 
freopen("log.txt", "w", stdout);

cout << "Welcome to GESP!" << endl;

fclose(stdout);

B.
std::ofstream outFile("log.txt");

outFile << "Welcome to GESP!" << endl;

outFile.close();




C.
std::ofstream outFile("log.txt");

cout << "Welcome to GESP!" << endl;

outFile.close();

D.
ofstream log_file("log.txt");

streambuf* org_cout = cout.rdbuf();

cout.rdbuf(log_file.rdbuf());

cout << "This output will go to the log file." << endl;

cout.rdbuf(oorg_cout);

【答案】C

【考纲知识点】文件输出重定向。

【解析】
选项 A:使用 freopen 函数将标准输出重定向到文件,然后进行输出,最后关闭标准输出,这种方式可以实现将输出重定向到文件。

选项 B:使用 ofstream 对象创建文件输出流,并通过该对象进行输出,然后关闭文件流,这种方式能正确将输出写入文件。

选项 C:创建了文件输出流 outFile ,但仍使用 cout 进行输出,此时 cout 的输出并不会重定向到文件,所以这种方式不能实现将字符串输出重定向到文件。

选项 D:通过修改 cout 的缓冲区,实现输出重定向到文件,然后再恢复原来的缓冲区,这种方式是有效的。

综上所述,不能实现将字符串输出重定向到文件的是选项 C 。

15.运行下面的代码,将出现什么情况?( )

double hmean(double a, double b) {

if (a == -b )

throw runtime_error("Runtime error occurred");

return 2.0*a*b/(a + b);

}

int main() {

double x = 10;

double y = -10;

try {

int result = hmean(x, y);

cout << "hmean: " << result << endl;

}

catch (const runtime_error& e) {

cout << "Caught: " << e.what() << endl;

} catch (...) {

cout << "Caught an unknown exception." << endl;

}

return 0;

}
A. 屏幕上输出 Caught: Runtime error occurred

B. 屏幕上输出 Caught an unknown exception

C. 程序调用 std::terminate()

D. 编译错误

【答案】A

【考纲知识点】异常处理。

【解析】
在 hmean 函数中,当 a 为 10 ,b 为 -10 时,条件 a == -b 成立,会抛出 runtime_error 类型的异常。

在 main 函数中,使用 try-catch 块来捕获异常。由于抛出的是 runtime_error 类型的异常,所以会被第一个 catch 块捕获,输出 Caught: Runtime error occurred 。

所以答案是 A 选项。

二、判断题(每题2分,共20分)

题目12345678910
答案

×

×

×

×

×

×

1.在 C++ 中,下面代码可以正确定义指针和初始化指针。
int* ptr;

*ptr = 10;

【答案】×

【考纲知识点】指针的定义和初始化。

【解析】在上述代码中,虽然定义了一个整型指针 ptr ,但是并没有给 ptr 指向一个有效的内存地址就直接对其解引用并赋值,这会导致未定义的行为,可能引发程序崩溃或产生不可预料的结果。

在使用指针之前,应该先让它指向一个有效的内存空间,例如通过动态分配内存(如 int* ptr = new int; )或者让它指向已存在的变量(如 int num = 10; int* ptr = # )。

所以这道题答案是 ×。

2.一个函数必须在调用之前既声明又定义。

【答案】×

【考纲知识点】函数的声明与定义。

【解析】在 C++ 中,函数可以先声明,然后在后续的代码中再进行定义。在调用一个函数时,只要在调用之前有该函数的声明,让编译器知道函数的参数类型和返回值类型,就可以进行调用。不一定非要在调用之前既声明又定义。

所以这道题答案是 ×。

3.函数参数可以通过值传递、引用传递和指针传递,这样函数内对参数的修改可以直接修改传入变量的值。

【答案】×

【考纲知识点】函数参数传递方式。

【解析】
在值传递方式中,函数内部对参数的修改不会影响到传入的变量本身。

而引用传递和指针传递,函数内对参数的修改可以直接影响到传入的变量的值。

但题目中说三种传递方式函数内对参数的修改都可以直接修改传入变量的值,这种说法是错误的。

所以这道题答案是 ×。

4.int arr[3][] 是一个正确的二维数组的声明。

【答案】×

【考纲知识点】二维数组的声明。

【解析】
在 C++ 中,声明二维数组时,除了第一维可以省略,其他维度都不能省略。

所以 int arr[3][] 这种声明方式是错误的。

因此这道题答案是 ×。

5.递推是一种通过已知的初始值和递推公式,逐步求解目标值的算法。

【答案】√

【考纲知识点】递推算法的概念。

【解析】
递推算法的核心思想就是利用已知的初始条件和明确的递推关系,通过逐步计算来得到最终的目标结果。

例如,斐波那契数列就是一个典型的递推问题,通过初始的两个数 0 和 1 ,以及递推公式 F(n) = F(n - 1) + F(n - 2) 来逐步求出后续的数。

所以 “递推是一种通过已知的初始值和递推公式,逐步求解目标值的算法” 这一表述是正确的。

6.某算法的递推关系式为T(n)=T(n-1)+n(n为正整数)及T(0)=1,则该算法的时间复杂度为O(n2)。

【答案】√

【考纲知识点】递推关系式与算法时间复杂度。

【解析】
已知递推关系式为 T(n) = T(n - 1) + n ,且 T(0) = 1 。

则:

T(1) = T(0) + 1 = 1 + 1 = 2

T(2) = T(1) + 2 = 2 + 2 = 4

T(3) = T(2) + 3 = 4 + 3 = 7

...

T(n) = T(n - 1) + n

依次类推,可以展开 T(n) :

T(n) = 1 + 1 + 2 + 3 +... + n

= 1 + n(n + 1) / 2

其时间复杂度主要由 n^2 项决定,所以该算法的时间复杂度为 O(n^2) 。

因此,这道题答案是√。

7.冒泡排序的平均时间复杂度为O(n2),但最优情况下为O(n)。

【答案】√

【考纲知识点】冒泡排序的时间复杂度。

【解析】
冒泡排序在最坏和平均情况下,比较和交换的次数都较多,时间复杂度为 O (n²)。

但在最优情况下,也就是数组已经完全有序时,只需要进行一轮比较,没有任何交换操作,时间复杂度为 O (n)。

所以这道题答案是√。

8.冒泡排序和插入排序都是稳定的排序算法。

【答案】√

【考纲知识点】稳定排序算法。

【解析】
稳定排序算法是指在排序过程中,如果两个元素的关键字相等,排序前后它们的相对顺序不变。

冒泡排序在相邻元素比较和交换时,如果两个元素相等则不会交换位置,因此是稳定的排序算法。

插入排序在将元素插入已排序部分时,如果遇到相等的元素,会将新元素插入到相等元素的后面,保持相等元素的相对顺序不变,所以也是稳定的排序算法。

综上,这道题答案是√。

9.选择排序是稳定的排序算法。

【答案】×

【考纲知识点】选择排序的稳定性。

【解析】
选择排序是不稳定的排序算法。

选择排序每次都从待排序序列中选择最小(或最大)的元素,然后与当前位置的元素交换。在这个过程中,如果存在相同的元素,它们的相对顺序可能会改变。

例如,序列 [5a, 8, 5b, 2, 9],第一次选择最小的元素 2 与第一个位置的 5a 交换,就改变了两个 5 的相对顺序。

所以这道题答案是 ×。

10.在 C++语言中,如果一个函数可能抛出异常,那么一定要在try 子句里调用这个函数。

【答案】×

【考纲知识点】异常处理中的 try 语句使用。

【解析】
在 C++ 中,如果一个函数可能抛出异常,不一定非要在 try 子句里调用这个函数。

可以在可能会抛出异常的函数被调用的任何地方,其上层的调用函数或者更外层的代码中使用 try-catch 结构来捕获和处理异常。

所以这道题答案是 ×。

三、编程题(每题25分,共50分)

1、Recamán

【问题描述】

小杨最近发现了有趣的 Recamán 数列,这个数列是这样生成的:
数列的第一项a1是1;
如果ak-1-k是正整数并且没有在数列中出现过,那么数列的第k项ak为ak-1-k,否则为ak-1+k。
小杨想知道 Recamán 数列的前n项从小到大排序后的结果。手动计算非常困难,小杨希望你能帮他解决这个问题。

【输入描述】

第一行,一个正整数n。

【输出描述】

一行,n个空格分隔的整数,表示 Recamán 数列的前n项从小到大排序后的结果。

【样例输入 1】

5

【样例输出 1】

1 2 3 6 7

【样例输入 2】

8

【样例输出 2】

1 2 3 6 7 12 13 20

【题目大意】

给定一个整数 n ,要求生成 Recamán 数列的前 n 项,并将其从小到大排序后输出。

【考纲知识点】
数组的运用。

条件判断语句。

冒泡排序算法。

【解题思路】
首先初始化第一项为 1 ,并标记已出现。

从第二项开始,根据规则计算当前项的值:如果前一项减当前项序号得到的结果为正且未出现过,当前项就是这个差值;否则当前项是前一项加当前项序号。同时标记当前项已出现。

最后使用冒泡排序对生成的数列进行排序并输出。

【参考程序解析
bubble_sort 函数实现冒泡排序,通过不断比较相邻元素并交换来将数组排序。

在 main 函数中,读入 n 后,逐步生成数列的每一项,同时标记出现过的数。然后调用冒泡排序,最后按要求输出数列。

【参考程序】
#include <cstdio>

#include <algorithm>

using namespace std;

const int N = 2e5 + 5;

const int C = 1e6 + 5;

int n;

int a[N];

int vis[C];

void bubble_sort(int *a, int n) {

bool flag = true;

while (flag) {

flag = false;

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

if (a[i] > a[i + 1]) {

flag = true;

int t = a[i];

a[i] = a[i + 1];

a[i + 1] = t;

}

}

}

}

int main() {

scanf("%d", &n);

a[1] = 1;

vis[1] = 1;

for (int i = 2; i <= n; i++) {

if (a[i - 1] - i <= 0 || vis[a[i - 1] - i])

a[i] = a[i - 1] + i;

else

a[i] = a[i - 1] - i;

vis[a[i]] = 1;

}

bubble_sort(a, n);

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

printf("%d%c", a[i], " \n"[i == n]);

return 0;

}

2、字符排序

【问题描述】

小杨有n个仅包含小写字母的字符串s1,s2,....,sn,小杨想将这些字符串按一定顺序排列后拼接到一起构成字符串t。小杨希望最后构成的字符串t满足:

假设ti为字符串t的第i个字符,对于所有的j<i均有tj<=ti 。两个字符的大小关系与其在字母表中的顺序一致,例如e<g<p<s。

小杨想知道是否存在满足条件的字符串排列顺序。

【输入描述】

第一行包含一个正整数T,代表测试数据组数。

对于每组测试数据,第一行包含一个正整数n,含义如题面所示。

之后n行,每行包含一个字符串si。

【输出描述】

对于每组测试数据,如果存在满足条件的排列顺序,输出 1,否则输出 0。

【样例输入 1】

3

3

aa

ac

de

2

aac

bc

1

gesp

【样例输出 1】

1

0

0

【题目大意】

给定多组测试数据,每组数据包含若干个仅包含小写字母的字符串。需要判断是否存在一种字符串的排列顺序,将它们按顺序拼接后,得到的新字符串满足从左到右字符依次非降序排列。

【考纲知识点】
字符串的处理。

冒泡排序算法。

字符比较。

【解题思路】

首先使用冒泡排序对输入的字符串数组进行排序。

然后将排序后的字符串依次拼接成一个新的字符串。

最后检查拼接后的字符串是否满足字符非降序的要求。
【参考程序解析
bubble_sort 函数实现了对字符串数组的冒泡排序。

在 main 函数中,通过循环处理多组测试数据。先读取字符串数量和各个字符串,进行排序和拼接,然后检查拼接后的字符串是否满足条件并输出结果。

【参考程序】
#include<bits/stdc++.h>

using namespace std;

string s[110];

void bubble_sort(string *a, int n) {

bool flag = true;

while (flag) {

flag = false;

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

if (a[i] > a[i + 1]) {

flag = true;

string t = a[i];

a[i] = a[i + 1];

a[i + 1] = t;

}

}

}

}

int main(){

int t;

cin>>t;

while(t--){

int n;

cin>>n;

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

cin>>s[i];

}

bubble_sort(s, n);

string t="";

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

t+=s[i];

}

int m = t.size();

int fl=1;

for(int i=1;i<m;i++){

if(t[i]<t[i-1]){

fl=0;

break;

}

}

cout<<fl<<"\n";

}

}

;