1 单选题(每题 2 分,共 30 分)
第1题 执行下面程序后,输出为( )。
cpp
int f(int x = 2){
return x * 3;
}
int main(){
cout << f() << " " << f(4);
}
A. 2 12 B. 6 12 C. 6 4 D. 12 6
解析:答案B。当用f()调用时,无实参,因此x为默认参数2,函数返回2*3为6。当用f(4)调用地,实参为4,因此x为4,函数返回4*3为12。故选B。
第2题 执行下面代码后,输出为( )。
cpp
int main() {
int a = 5;
int* p = &a;
int** q = &p;
**q += 7;
cout << a << " " << *p;
}
A. 5 5 B. 12 12 C. 12 5 D. 5 12
解析:答案B。a为整型变量,p为指针,q为指针的指针,q指向p指针,p指向a。**q += 7; 先是*q 解引用q得到指针p,**q 解引用p得到变量a的值。**q += 7 相当于 a += 7,即a的值从5变为12,*p等价a。故选B。
第3题 已知:
cpp
int a[3][4] = {
{1,2,3,4},
{5,6,7,8},
{9,10,11,12}
};
int (*p)[4] = a;
则表达式 *(*(p + 2) + 1) 的值为( )。
A. 6 B. 10 C. 9 D. 11
解析:答案B。p为二维数组的行指针,p + 2 表示指向第3行(索引为2)的指针。*(p + 2) 表示第3行的首地址,即a[2][0]的地址。*(p + 2) + 1 表示第3行的第2个元素的地址(a[2][1]的地址)。*(*(p + 2) + 1) 表示第3行第2个元素的值,即a[2][1] = 10。故选B。
第4题 执行下面程序后,输出为( )。
cpp
void fun(int a, int &b, int *c){
a += 1;
b += 2;
*c += 3;
}
int main(){
int x = 1, y = 1, z = 1;
fun(x, y, &z);
cout << x << " " << y << " " << z;
}
A.2 3 4 B. 1 3 4 C. 2 1 4 D. 1 1 1
解析:答案B。C++函数参数值传递、引用传递和指针传递。本题a为值传递,与实参无关联,b为引用传递,b相当于是实参的副本或别名,c为指针传递,c指向实参。当x = 1, y = 1, z = 1时调用fun(x, y, &z),对a的修改不影响x。对b的修改等价于修改y,b接收的值为1,+2后b等于3,影响y,y也等于3。c为指向z的指针,对指针的操作会反映到z,*c += 3,相当于z+=3,z=4。所以输出为1 3 4,故选B。
第5题 执行下面程序后输出为( )。
cpp
int x = 3;
void f(int& x){
x += 2;
}
int main(){
int x = 10;
f(x);
cout << x << " " << ::x;
}
A. 12 3 B. 10 5 C. 12 5 D. 10 3
解析:答案A。函数f参数引用传递,对函数中的x的操作等价于对不函数的x的操作,两变量同地址(引用传递)。::x指全局变量x。本题行7行的f(x)中的x为主函数中的x,值为10,在函数中+2,x值为12,全局变量x的值为3,所以输出为12 3。故选A。
第6题 下列关于结构体初始化的写法,正确的是( )。
A.
cpp
struct Point { int x, y; };
Point p = (1,2);
B.
cpp
struct Point { int x, y; };
Point p = {1,2};
C.
cpp
struct Point { int x, y; };
Point p = new Point(1,2);
D.
cpp
struct Point { int x, y; };
Point p = <1,2>;
解析:答案B。在C++/C中在定义结构体、数组时初始化可以通过花括号{}。
选项A:Point p = (1,2); 在 C/C++ 中,圆括号()用于函数调用或构造函数初始化,不能用于结构体的初始化。这种写法在C++中会报错,即使在C中也并非标准用法,所以错误。
选项B是正确的结构体初始化方式,所以正确。
选项C:Point p = new Point(1,2); new是C++中用于动态分配内存的关键字,用于创建对象并返回指针。而此处 Point p 是一个栈上的对象,不能使用new来初始化。如果要使用new,应为指针类型,如 Point* p = new Point(1,2); ,所以错误。
选项D:Point p = <1,2>; C/C++中没有<1,2>这种初始化语法。这种写法在语法上是非法的,所以错误。故选B。
第7题 执行下面代码后输出为( )。
cpp
struct S { int a; int b; };
void g(S s){ s.a += 10; }
void h(S& s){ s.b += 10; }
int main(){
S s{1,2};
g(s);
h(s);
cout << s.a << " " << s.b;
}
A. 11 12 B. 1 12 C. 11 2 D. 1 2
解析:答案B。本题程序struct S定义了一个结构体,包含两个整型成员a和b。函数 g(S s) 接收一个S类型的值参数,在函数内部对s.a的修改不会影响原始变量s(主函数中s),因为传入的是副本。函数 h(S& s)接收一个S类型的引用参数。因此对s.b的修改会直接影响原始变量s(主函数中s)。
在主函数main()中:初始化 s{1, 2},即 s.a=1, s.b=2。调用g(s)后,s.a不变,仍为1。调用h(s)后,s.b增加10,主函数的s.b也变为12。最终输出s.a和s.b的值,即1 12。故选B。
第8题 关于递推算法的描述,正确的是( )。
A. 递推表现为函数自己调用自己
B. 递推从已知初值出发,利用递推关系逐步推出后续结果
C. 递推只能用于指数复杂度问题
D. 递推一定需要回溯
解析:答案B。递推算法的基本思想是通过定义初始条件(或边界条件)和递推关系式,从起点开始,按照固定规律迭代求解,直至满足终止条件。根据推导方向的不同,不要分为顺推法和逆推法两种方法。
选项A为递归算法特征,所以错误。选项B符合递推算法的基本思想,所以正确。选项C递推算法并不局限于指数复杂度问题,递推算法的应用场景多样,关键在于找到相邻数据项之间的关系。常用于数值计算、组合计数问题、优选法等领域,所以错误。选项D递推不一定需要回溯,递推通常是从已知条件正向推导,而回溯是另一种算法思想,所以错误。故选B。
第9题 执行 climb(6) 的返回值为( )。
cpp
int climb(int n){
if(n <= 2) return n;
int a = 1, b = 2, c = 0;
for(int i = 3; i <= n; i++){
c = a + b;
a = b;
b = c;
}
return c;
}
A. 8 B. 13 C. 5 D. 10
解析:答案B。为是个类斐波那契数列,只不过f(1)=1, f(2), f(n)=f(n-1)+f(n-2), i=3, 4, ..., n。前六项:1, 2, 3, 5, 8, 13,climb(6)=13。
程序推算:a=1, b=2, i从3到6,当i=3时c=a+b=1+2=3, a=b=2, b=c=3;
当i=4时c=a+b=2+3=5, a=b=3, b=c=5;
当i=5时c=a+b=3+5=8, a=b=5, b=c=8;
当i=6时c=a+b=5+8=13, a=b=8, b=c=13;
return c; 返回13,故选B。
第10题 某排序算法对如下数据排序(按 score 升序),则下面关于该排序算法稳定性的描述中,说法正确的是( )。
初始: (90,'A'), (90,'B'), (80,'C'), (90,'D')
排序后:(80,'C'), (90,'A'), (90,'B'), (90,'D')
A. 不稳定,因为出现了相同分数
B. 稳定,因为相同 score 的相对顺序保持为 A 在 B 前、B 在 D 前
C. 不稳定,因为C跑到前面了
D. 无法判断
解析:答案B。题目按数据排序(按score升序),所以(80,'C')排在前面,正确;同是90分的三个成绩,排序后次序未变,仍是(90,'A')在前, 其次是(90,'B'), (90,'D')排最后,所以是稳定排序,选项B正确。故选B。
第11题 下面代码试图把数组按升序进行"插入排序",横线处应填写( )。
cpp
void ins(int a[], int n){
for(int i = 1; i < n; i++){
int key = a[i];
int j = i-1;
while(j >= 0 && __________){
a[j+1] = a[j];
j--;
}
a[j+1] = key;
}
}
A. a[j] < key B. a[j] > key C. a[j+1] > key D. a[j] == key
解析:答案B。插入排序,想找位置是向左比较,直到找到一个不大于待插入元素的元素,或者到达已排序部分的开始位置。所以元素要后移的条件是大于待插入元素的元素,即a[j] > key。故选B。
第12题 下列代码段的时间复杂度为( )。
cpp
int cnt=0;
for(int i=0; i<n; i++){
for(int j=0; j<n; j++){
if( (i+j) % 3 == 0) cnt++;
}
}
A. 𝑂(𝑛) B. 𝑂(𝑛log𝑛) C. 𝑂(𝑛²) D. 𝑂(2ⁿ)
解析:答案C。代码为双重循环嵌套,时间复杂度为𝑂(𝑛²)。故选C。
第13题 下面哪种方式不能实现将字符串 Welcome to 2026! 输出重定向到文件 log.txt( )。
A.
cpp
freopen("log.txt", "w", stdout);
cout << "Welcome to 2026!" << endl;
fclose(stdout);
B.
cpp
std::ofstream outFile("log.txt");
cout << "Welcome to 2026!" << endl;
outFile.close();
C.
cpp
ofstream log_file("log.txt");
streambuf* org_cout = cout.rdbuf();
cout.rdbuf(log_file.rdbuf());
cout << "Welcome to 2026!" << endl;
cout.rdbuf(org_cout);
D.
cpp
std::ofstream outFile("log.txt");
outFile << "Welcome to 2026!" << endl;
outFile.close();
解析:答案B。选项A中在使用freopen("log.txt", "w", stdout)将标准输出重定向到文件后,cout可以输出到文件,但fclose(stdout)会关闭文件流,这会使得后续的cout操作无法正常工作,可能导致程序崩溃或未定义行为。
选项B使用std::ofstream outFile("log.txt")创建文件流,通过outFile << "Welcome to 2026!" << endl可以向文件写入内容,但用cout不行,因为没有重定向,cout向标准输出设备输出,故错误。
选项C通过cout.rdbuf()和outFile.rdbuf()进行流缓冲区重定向,这是C++推荐的重定向方法,避免了freopen可能带来的同步问题。
选项D直接使用std::ofstream对象写入文件,也是标准的C++文件操作方式(参考选项B解析)。
故选B。
第14题 执行下面程序,输出结果是( )。
cpp
int divi(int a,int b){
if(b==0) throw 0;
return a/b;
}
int main(){
try{
cout << divi(10,0);
}catch(const char* msg){
cout << "A";
}catch(int){
cout << "B";
}
}
A. A B. B C. 程序崩溃 D. 无输出
解析:答案B。C++的try-catch异常处理结构,如在try中有throw抛出异常,会用catch语句来捕获。代码中有两个 catch 块:第一个 catch(const char* msg):用于捕获类型为 const char* (常量字符的指针)的异常。第二个 catch(int):用于捕获类型为 int 的异常。
由于在 divi 函数中抛出的是 int 类型的异常(throw 0),因此会匹配第二个 catch(int) 块,输出 "B"。故选B。
第15题 下列函数实现排行榜中单个元素的位置调整(类似插入排序的相邻搬移)。当某玩家分数增加,需将其向前移动时, while 循环的条件应为( )。
cpp
struct Player{ int score; };
void up(Player players[], int n, int idx){
Player cur = players[idx];
int i = idx;
while( ____________________ ){
players[i] = players[i-1];
i--;
}
players[i] = cur;
}
A. i > 0 && cur.score > players[i-1].score
B. i > 0 && cur.score < players[i-1].score
C. i < n-1 && cur.score > players[i+1].score
D. i < n-1 && cur.score < players[i+1].score
解析:答案A。本题的函数实现的是排行榜中单个元素的位置调整,类似于插入排序的相邻搬移。当某玩家分数增加时,需要将其向前移动。让我们分析函数逻辑:
cur = players[idx] - 保存当前要移动的玩家
i = idx - 从当前位置开始向前比较
while( ____________________ ) - 循环条件决定何时继续向前移动
选项A,i > 0---确保不越界,不能移动到数组开头之前,cur.score > players[i-1].score---当当前玩家分数大于前一个玩家分数时,需要向前移动。在排行榜中,分数高的排在前面。当一个玩家分数增加后,如果他的分数比前面的玩家高,就需要向前移动,直到找到合适的位置。所以正确。
选项B条件相反,会形成降序排列,所以错误。
选项C使用了向后比较,不符合题目要求的向前移动,所以错误。
选项D同样使用向后比较,且条件也不符合,所以错误。
故选A。
2 判断题(每题 2 分,共 20 分)
第1题 下面代码执行结束时,变量 a 的值变成 15。
cpp
void add10(int &x) { x += 10; }
int main() {
int a = 5;
add10(a);
}
解析:答案正确(√)。add10参数为引用传递,对形参的修改等价于对实参的操作。x=5+10=15,即a=15。故正确。
第2题 引用一旦绑定某个变量,就不能再绑定其他变量。( )
解析:答案正确(√)。在C++中,引用(包括函数参数中的引用)必须在定义时初始化并绑定到一个有效变量,且一旦绑定后,其生命周期内不能再重新绑定到其他变量。这体现了引用的"别名"本质。故正确。
第3题 执行下面代码,输出结果为 5 。
cpp
int main() {
int a[2][3];
cout << &a[1][2] - &a[0][1] << endl;
return 0;
}
解析:答案错误(╳)。此&引用为地址,地址-地址为元素个数。&a[1][2] - &a[0][1]=4。故错误。
第4题 下面程序可以正常编译并输出 10 。
cpp
int calc(int x, int y = 10);
int calc(int x) { return x * 2; }
int calc(int x, int y) { return x * y; }
int main() {
cout << calc(5);
}
解析:答案错误(╳)。根据函数重载的规则,函数重载的关键在于参数列表的不同,而返回值类型不同不能构成函数重载。
在给出的代码中,存在函数重载冲突,调用歧义:
当调用 calc(5) 时,编译器无法确定应该调用哪个函数,因为:可以调用 int calc(int x)(参数匹配),也可以调用 int calc(int x, int y = 10)(第一个参数匹配,第二个参数使用默认值),所以错误。故错误。
第5题 下面程序执行后输出 2010 。
cpp
int x = 10;
void f() { int x = 20; cout << x; }
int main() {
f();
cout << x;
}
解析:答案正确(√)。f()函数中定义的变量x为局部变量,cout输出的是局部变量x,输出20。函数返回后,局部变量x已不存在,cout输出的是全局变量x,输出10,合并输出2010,所以正确。故正确。
第6题 在 C++ 中,如果声明了一个指针变量但没有显式初始化,该指针会自动被初始化为 nullptr 。
解析:答案错误(╳)。在C++中,如果声明了一个指针变量但没有显式初始化,该指针一般不会自动被初始化为 nullptr。只有全局变量和静态变量(包括静态局部变量)才会被默认初始化为 nullptr。故错误。
第7题 下面代码没有语法错误。
cpp
struct GameCharacter {
string name;
int level;
float position_x;
float position_y;
struct Equipment {
string weapon;
int attack_bonus;
int defense_bonus;
} equipment;
struct Skill {
string name;
int damage;
} skills[8];
int skill_count;
};
解析:答案正确(√)。C++允许在函数内部定义结构体(struct),结构体内嵌套结构体。GameCharacter及其嵌套结构体 Equipment 和 Skill 的定义均符合语法规则:
外层结构体 GameCharacter 包含基本成员(name, level, 坐标等)。
嵌套结构体 Equipment 定义了 weapon 等成员,并直接声明了结构体变量 equipment。
嵌套结构体 Skill 定义了name等成员,并直接声明了结构体数组成员 skills[8]。
skill_count 是GameCharacter的整数成员。
结构体定义以分号(};)正确结束,符合 C++ 语法要求。
成员声明和类型使用均合法,未违反声明规则。故正确。
第8题 下面程序能够把 Hello 写入 data.txt 文件中。
cpp
ofstream fout("data.txt");
cout << "Hello";
fout.close();
解析:答案错误(╳)。ofstream fout("data.txt") 创建了文件输出流对象 fout,但后续操作cout << "Hello" 将字符串输出到标准控制台(std::cout),而非文件流 fout。因此,文件 data.txt 会被创建(若不存在)或清空(若存在),但内容始终为空。故错误。
第9题 由于选择排序和插入排序的时间复杂度均为 ,在任何实际场景下两者的性能表现几乎相同,可以互相替代。
解析:答案错误(╳)。虽然选择排序和插入排序的平均时间复杂度均为𝑂(𝑛²),但两者在实际场景中的性能表现存在显著差异,无法互相替代。
插入排序:最好情况(数据已有序)时间复杂度为𝑂(𝑛),仅需n−1次比较且无需移动元素。最坏情况(数据完全逆序)时间复杂度为𝑂(𝑛²)。
选择排序:无论数据是否有序,时间复杂度始终为𝑂(𝑛²)(固定执行约(n−1)/2 次比较和n−1次交换)。插入排序在部分有序的数据中表现远优于选择排序。故错误。
第10题 下面用递推方式计算斐波那契数列第n项的程序,时间复杂度是𝑂(2ⁿ)。
cpp
int fib(int n) {
if (n <= 1) return n;
int f0 = 0, f1 = 1, cur = 0;
for (int i = 2; i <= n; i++) {
cur = f0 + f1;
f0 = f1;
f1 = cur;
}
return cur;
}
解析:答案错误(╳)。题目所给程序仅一重从2到n的循环,所以时间复杂度为𝑂(𝑛),递归计算斐波那契数列第n项的时间复杂度为𝑂(2ⁿ)。故错误。
3 编程题(每题 25 分,共 50 分)
3.1 编程题1
- 试题名称:山之谷
- 时间限制:1.0 s
- 内存限制:512.0 MB
3.1.1题目描述
现有一片山地,可以视为一个𝑁行𝑀列的网格图,第𝑖行𝑗列的海拔为ℎᵢⱼ。
如果一个单元格的海拔不高于其所有相邻单元格(相邻包括上、下、左、右、左上、右上、左下、右下,最多8个方向)的海拔,则称该单元格为山谷。
请你数一数该片山地中有多少山谷。
3.1.2 输入格式
第一行包含2个整数𝑁, 𝑀,表示山地的大小。
之后𝑁行,每行包含𝑀个整数ℎᵢ₁, ℎᵢ₂, ..., ℎᵢₘ,表示海拔。
3.1.3 输出格式
输出 1 行,包含1个整数𝐶,表示山谷的数量。
3.1.4 样例
3.1.4.1 输入样例
3 5
7 6 6 7 9
6 5 6 7 6
6 5 7 8 9
3.1.4.2 输出样例
3
3.1.5 样例解释
样例1如图所示,绿色单元格代表山谷:

3.1.6 数据范围
保证1≤𝑁, 𝑀≤100,1≤ℎᵢⱼ≤10⁵。
3.1.7 参考程序
解析:由所给可知边界外算高海拔区,本题要判其网格的上、下、左、右、左上、右上、左下、右下8个方向,及边界处理。为简化边界处理,可在网格外"筑"一圈高海拔"山崖"网格,值大于最大海拔10⁵即可。如图1所示,浅蓝色为填充网格,对网格(1,1)值为7,其八个方向区域如红色虚线框中所示,这样边界网格可以与中间正常网格一样进行处理。

如图1 在数据网格外加一圈高海拔网格

图2网格(i,j)八个方向位置偏移量
方法一:
思路:设处理网格(i,i)时要处理周边八个点的位置偏移量如图2所示。可将这八个位置偏移量,保存在mark[8][2]={{-1, -1}, {-1, 0}, {-1, 1}, {0, -1}, {0, 1}, {1, -1}, {1, 0}, {1, 1}}中,则第k(k=0,1,..,7)个网格为(i+mark[k][0], j+mark[k][1]),只要网格(i,i)高度高于这八个之一的网格高度就不是山谷,只有网格(i,i)高度不高于这全部八个网格高度时才是山谷。按此方法编写的参考代码如下(洛谷AC通过):
cpp
#include <iostream>
using namespace std;
int main() {
int n, m, cnt = 0, k; // cnt存放结果,必须初始化为0,否则结果不可预测
int h[105][105]; // N,M≤100 ↓ mark为网格八个方向的偏移量
int mark[8][2] = {{-1, -1}, {-1, 0}, {-1, 1}, {0, -1}, {0, 1}, {1, -1}, {1, 0}, {1, 1}};
cin >> n >> m;
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++)
cin >> h[i][j];
for (int i = 0; i <= max(n, m) + 1; i++)
h[i][0] = h[0][i] = h[i][m + 1] = h[n + 1][i] = 1e6; // ℎᵢⱼ≤10⁵<10⁶
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++) {
for (k = 0; k < 8; k++)
if (h[i][j] > h[i + mark[k][0]][j + mark[k][1]]) break;
if (k == 8) cnt ++; // k循环正常结束时k=8,有break退出k<8
}
cout << cnt;
return 0;
}
方法二:
思路:网格的八个方向计算用下个-1到1的循环,实际计算九个网格(包含待测网格本身),由于待测网格高度等于自身高度,所以对待测网格不用特别处理。
设处理网格(i,j)时要处理周边八个点的位置偏移量如图2所示。可
cpp
for (x = -1; x <= 1; x++) {
for (y = -1; y <= 1; y++)
if (h[i][j] > h[i + x][j + y]) break;
if (y < 2) break; // y循环正常结束y=2,被break不用再判其他网格
}
if (x > 1 && y > 1) cnt ++; // x、y循环正常结束时x=y=2
只要网格(i,j)高度高于这九个(含网格(i,j))之一的网格高度就不是山谷,只有网格(i,j)高度不高于这全部九个网格高度时才是山谷。按此方法编写的参考代码如下(洛谷AC通过):
cpp
#include <iostream>
using namespace std;
int main() {
int n, m, cnt = 0, x, y; // cnt存放结果,必须初始化为0,否则结果不可预测
int h[105][105]; // N,M≤100
cin >> n >> m;
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++)
cin >> h[i][j];
for (int i = 0; i <= max(n, m) + 1; i++)
h[i][0] = h[0][i] = h[i][m + 1] = h[n + 1][i] = 1e6; // ℎᵢⱼ≤10⁵<10⁶
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++) {
for (x = -1; x <= 1; x++) {
for (y = -1; y <= 1; y++)
if (h[i][j] > h[i + x][j + y]) break;
if (y < 2) break; // y循环正常结束y=2,否则就不用再判其他方向
}
if (x > 1 && y > 1) cnt ++; // x、y循环正常结束时x=2、y=2
}
cout << cnt;
return 0;
}
3.2 编程题2
- 试题名称:礼盒排序
- 时间限制:1.0 s
- 内存限制:512.0 MB
3.2.1题目描述
商店推出了许多礼盒,每个礼盒中包含 k 件商品,每件商品都有一个价格。
现在需要对这些礼盒进行排序,排序规则如下:
-
先按礼盒总价格从小到大排序;
-
如果总价格相同,按礼盒中最贵商品的价格从小到大排序;
-
如果仍然相同,按礼盒中最便宜商品的价格从小到大排序;
-
如果仍然相同,按礼盒编号从小到大排序。
请输出排序后的礼盒编号。
3.2.2 输入格式
第一行包含两个整数𝑛和𝑘,分别表示礼盒数量和每个礼盒中商品的数量。
接下来𝑛行,每行包含𝑘个整数,第𝑖行表示第𝑖个礼盒中各商品的价格。
3.2.3 输出格式 输出一行,包含排序后的礼盒编号(编号从1开始),用空格分隔。
3.2.4 样例
3.2.4.1 输入样例
cpp
4 3
3 5 2
4 1 5
2 2 4
3 4 3
3.2.4.2 输出样例
cpp
3 4 2 1
3.2.5 样例解释
4个礼盒分别为:

排序过程:
-
按总价排序,3号礼盒总价最小;
-
其余总价均为 10,再按最大值排序,4号最大值更小;
-
1号和2号最大值相同,再按最小值排序,2号更小。
最终顺序为:3 4 2 1
3.2.6 数据范围
保证1≤𝑛≤10³, 1≤𝑘≤10,商品价格≤10⁴。
3.2.7 参考程序
解析:本题涉及多维度排序,最简单的办法是使用C++标准模板库(STL)的<algorithm>头文件中的sort函数,当然也可以自己编一个常用排序算法编写一个多维度排序程序。由于排序规则为:1)先按礼盒总价格从小到大排序;2)如果总价格相同,按礼盒中最贵商品的价格从小到大排序;3)如果仍然相同,按礼盒中最便宜商品的价格从小到大排序;4)如果仍然相同,按礼盒编号从小到大排序。所以需要一个结构体来存储每个礼盒的编号、总价、最高价、最低价。对各礼盒中各商品的价格不用保存,但需计算礼盒中商品的总价、最高价、最低价,然后保存礼盒的编号、总价、最高价、最低价。
方法一:
用结构体数组
cpp
struct box {
int bh, sum, max, min; // 礼盒编号、总价、最高价、最低价
} giftbox[1005];
存储𝑛个礼盒的相关信息,会后用C++标准模板库(STL)的、<algorithm>头文件中的sort()函数排序,对sort()函数,默认是对第一项升序排序,否则需提供排序规则函数cmp()函数。
cpp
bool cmp(const box &a, const box &b) {
if (a.sum != b.sum) return a.sum < b.sum; // 先比较成员sum
if (a.max != b.max) return a.max < b.max; // 如成员sum相等则再比较成员max
if (a.min != b.min) return a.min < b.min; // 如成员sum相等、成员max相等则再比较成员min
return a.bh < b.bh; // 如sum相等、成员max相等、成员min相等则比较成员bh
}
参考代码如下(洛谷AC通过):
cpp
#include<iostream>
#include<algorithm>
using namespace std;
struct box { // 定义结构体存储每个礼盒信息
int bh, sum, max, min; // 礼盒编号、总价、最高价、最低价
} giftbox[1005]; // 定义结构体数组,存储各礼盒信息(n≤10³<1005)
bool cmp(const box &a, const box &b) { // 给STL中的sort()的比较函数
if (a.sum != b.sum) return a.sum < b.sum; // 先比较成员sum
if (a.max != b.max) return a.max < b.max; // 如成员sum相等则再比较成员max
if (a.min != b.min) return a.min < b.min; // 如成员sum相等、成员max相等则再比较成员min
return a.bh < b.bh; // 如sum相等、成员max相等、成员min相等则比较成员bh
}
int main() {
int n, k, b; // n=礼盒数,k=每个礼盒的商品(礼物)数
cin >> n >> k; // 输入礼盒数和每个礼盒的商品数
// 读取每个礼盒的商品价格,计算总价、最高价和最低价
for (int i = 0; i < n; i++) {
giftbox[i].bh = i + 1; // 给第i个礼盒编号(编号从1开始)
giftbox[i].sum = 0; // 初始化总价为0
for (int j = 1; j <= k; j++) { // 输入k个商品(礼物)
cin >> b; // 读取当前商品(礼物)价格
giftbox[i].sum += b; // 累加总价
if (j == 1) { // 初始化该盒首个礼物的最高价、最低价
giftbox[i].max = b;
giftbox[i].min = b;
continue; // 跳过后续比较,处理下一个礼物
}
if (b > giftbox[i].max) // 求最高价
giftbox[i].max = b;
else if (b < giftbox[i].min) // 求最低价
giftbox[i].min = b;
}
}
sort(giftbox, giftbox + n, cmp); // 排序
for (int i = 0; i < n; i++) // 输出排序后的礼盒编号
if (i == 0) cout << giftbox[i].bh; // 确保最后无空格
else cout << " " << giftbox[i].bh;
return 0;
}
方法二:
在方法一的基础上自编多维排序,此处用插入排序为例。普通插入排序函数如下:
cpp
void insertSort(int array[],int length){
for(int i = 1;i < length;i++){ // 从第2个元素开始(第1个元素视为已排序)
int key = array[i]; // 保存当前需要插入的元素
int j = i;
while(j>0 && key < array[j-1]){ // 将比key大的元素向后移动
array[j] = array[j-1];
j--;
}
array[j] = key;
}
}
本题多维排序:
cpp
void insertSort(int array[],int length){
for(int i = 1;i < length;i++){ // 从第2个元素开始(第1个元素视为已排序)
int key = array[i]; // 保存当前需要插入的元素
int j = i;
while(j>0 && key.sum < array[j-1].sum) { // 将比key大的元素向后移动
array[j] = array[j-1];
j--;
}
while(j>0 && key.sum == array[j-1].sum && key.max < array[j-1].max){ // 将比key大的元素向后移动
array[j] = array[j-1];
j--;
}
while(j>0 && key.sum == array[j-1].sum && key.max == array[j-1].max && key.min < array[j-1].min){ // 将比key大的元素向后移动
array[j] = array[j-1];
j--;
} // 最低价仍相同则保留原始顺序(插入排序为稳定排序)
array[j] = key;
}
}
参考代码如下(洛谷AC通过):
cpp
#include<iostream>
using namespace std;
struct box { // 定义结构体存储每个礼盒信息
int bh, sum, max, min; // 礼盒编号、总价、最高价、最低价
} giftbox[1005]; // 定义结构体数组,存储各礼盒信息(n≤10³<1005)
int main() {
int n, k, b; // n=礼盒数,k=每个礼盒的商品(礼物)数
cin >> n >> k; // 输入礼盒数和每个礼盒的商品数
// 读取每个礼盒的商品价格,计算总价、最高价和最低价
for (int i = 0; i < n; i++) {
giftbox[i].bh = i + 1; // 给第i个礼盒编号(编号从1开始)
giftbox[i].sum = 0; // 初始化总价为0
for (int j = 1; j <= k; j++) { // 输入k个商品(礼物)
cin >> b; // 读取当前商品(礼物)价格
giftbox[i].sum += b; // 累加总价
if (j == 1) { // 初始化该盒首个礼物的最高价、最低价
giftbox[i].max = b;
giftbox[i].min = b;
continue; // 跳过后续比较,处理下一个礼物
}
if (b > giftbox[i].max) // 求最高价
giftbox[i].max = b;
else if (b < giftbox[i].min) // 求最低价
giftbox[i].min = b;
}
} // 以下为多维插入排序
for (int i = 1; i < n; i++) { // 从第2个元素开始(第1个元素视为已排序)
box key = giftbox[i]; // 保存当前需要插入的元素
int j = i;
while (j > 0 && key.sum < giftbox[j - 1].sum) { // 将比key大的元素向后移动
giftbox[j] = giftbox[j - 1];
j--;
}
while (j > 0 && key.sum == giftbox[j - 1].sum && key.max < giftbox[j - 1].max) { // 将比key大的元素向后移动
giftbox[j] = giftbox[j - 1];
j--;
}
while (j > 0 && key.sum == giftbox[j - 1].sum && key.max == giftbox[j - 1].max && key.min < giftbox[j - 1].min) { // 将比key大的元素向后移动
giftbox[j] = giftbox[j - 1];
j--;
} // 最低价仍相同则保留原始顺序(插入排序为稳定排序)
giftbox[j] = key;
}
for (int i = 0; i < n; i++) // 输出排序后的礼盒编号
if (i == 0) cout << giftbox[i].bh; // 确保最后无空格
else cout << " " << giftbox[i].bh;
return 0;
}