一、蛇形矩阵


cpp
#include<iostream>
using namespace std;
int main(){
int N; // 定义一个整数变量N,用于存储用户输入的数字
cin>>N; // 从键盘输入N的值(决定输出多少行和列)
int v[105][105]; // 定义一个105行105列的二维整数数组v(预留足够空间)
v[0][0]=1; // 设置数组第一个元素(左上角)的值为1
// 第一个循环:填充第一列的数据
for(int i=1 ; i<100 ;i++){
v[i][0] = v[i-1][0]+i; // 当前行的第一列 = 上一行的第一列 + 当前行号
// 这样第一列形成数列:1, 2, 4, 7, 11, 16, ...
}
int zero_one=2; // 定义一个变量zero_one,初始值为2(用于控制每行起始的间隔)
// 第二个循环:填充整个二维数组的所有列
for(int i=0 ; i<100 ;i++){ // 遍历每一行(0到99行)
int interval=zero_one; // 设置当前行的起始间隔为zero_one的值
for(int j=1 ; j<100 ;j++){ // 遍历每一列(从第1列开始到第99列)
v[i][j] = v[i][j-1]+interval; // 当前元素 = 左边相邻元素 + 当前间隔
interval++; // 间隔递增1(每向右一列,间隔+1)
}
zero_one++; // 每完成一行,zero_one加1(下一行的起始间隔更大)
}
int lie=N; // 定义一个变量lie,初始值为N(控制每行输出的列数)
// 第三个循环:按照特定格式输出数组内容
for(int i=0 ; i<N ;i++){ // 输出前N行(i从0到N-1)
for(int j=0 ;j<lie;j++){ // 输出当前行的前lie列(lie逐渐减少)
cout<<v[i][j]<<" "; // 输出数组元素,后面跟一个空格
}
lie--; // 每行输出完后,lie减1(下一行少输出一列)
cout<<endl; // 每行结束后换行
}
return 0;
}
二、淘淘摘苹果


cpp
#include<iostream>
using namespace std;
int main(){
int a[10]; // 定义一个长度为10的整型数组a,用于存储10个整数
// 第一个循环:读取10个整数存入数组
for(int i=0 ; i<10 ;i++){ // 循环10次,i从0到9
cin>>a[i]; // 从键盘输入一个整数,存入数组的第i个位置
}
int arm; // 定义一个整数变量arm(表示手臂长度)
cin>>arm; // 从键盘输入arm的值
int getlong=arm+30; // 定义一个变量getlong,值为arm + 30
// 是手臂长度加上30厘米(拿东西的最大高度)
int sum=0; // 定义一个计数器sum,初始值为0
// 第二个循环:统计数组中有多少个元素不超过getlong
for(int i=0 ; i<10 ;i++){ // 遍历数组的10个元素
if(a[i]<=getlong){ // 如果当前元素的值小于等于getlong
sum++; // 计数器加1
}
}
cout<<sum; // 输出统计结果(有多少个数字不超过arm+30)
return 0;
}
三、明明的随机数


cpp
#include<iostream>
#include<algorithm> // 包含算法库,用于sort排序函数
using namespace std;
int main(){
int N; // 定义一个整数变量N,表示要输入的数字个数
cin>>N; // 从键盘输入N的值
int a[N]; // 定义一个长度为N的数组a(变长数组,用于存储原始数据)
// 第一个循环:读取N个整数存入数组a
for(int i=0 ; i<N ;i++){
cin>>a[i];
}
sort(a, a+N); // 使用sort函数对数组a进行排序
// a是数组首地址,a+N是尾地址,将a[0]到a[N-1]按升序排序
int b[N]; // 定义一个长度为N的数组b,用于存储去重后的结果
b[0]=a[0]; // 将排序后数组的第一个元素存入b的第一个位置
int tag=1; // 定义一个计数器tag,初始值为1,表示b数组中已有1个元素
// 第二个循环:遍历排序后的数组a,进行去重操作
for(int i=1 ; i<N ;i++){ // 从第1个元素开始遍历到最后一个(i从1到N-1)
if(a[i]!=a[i-1]){ // 如果当前元素不等于前一个元素(发现新数字)
b[tag]=a[i]; // 将当前元素存入b数组的第tag个位置
tag++; // tag加1,表示又找到一个不重复的元素
}
// 如果a[i]等于a[i-1],说明是重复元素,跳过不处理
}
cout<<tag<<endl; // 输出去重后的元素个数(即不重复数字的个数)
// 第三个循环:输出b数组中存储的去重后的所有元素
for(int i=0 ; i<tag ;i++){ // 循环tag次,输出所有不重复的数字
cout<<b[i]; )
}
return 0;
}
// 只对前5个元素排序 sort(arr, arr + 5);
// 只对第3到第7个元素排序(索引2到6) int arr2[] = {5, 2, 8, 1, 9, 3, 7, 4, 6}; sort(arr2 + 2, arr2 + 7);C++标准库采用左闭右开区间 [start, end):
包含起始地址指向的元素
不包含结束地址指向的元素
四、奖学金




cpp
#include<iostream> // 包含输入输出流库,用于cin和cout
#include<cmath> // 包含数学函数库,用于pow()幂运算
#include<algorithm> // 包含算法库,用于sort排序
using namespace std; // 使用标准命名空间
// 自定义比较函数,用于降序排序
bool cmp(int a , int b){
return a > b; // 返回a>b表示降序排列
}
int main(){
int nn, numbers = 1; // nn:学生人数, numbers:用于记录nn的位数
cin >> nn; // 输入学生人数
int p = nn; // 将nn赋值给p,用于计算位数
// 计算nn的位数(例如:nn=100,则numbers=3)
while(p / 10 != 0){ // 当p除以10不等于0(即p还有更多位)
numbers++; // 位数加1
p = p / 10; // p除以10,去掉最后一位
}
// 循环结束后,numbers就是nn的位数
int a[nn]; // 定义数组a,用于存储编码后的学生信息
// 输入每个学生的成绩并编码
for(int i = 0; i < nn; i++){
int x, y, z; // x:语文, y:数学, z:英语
cin >> x >> y >> z;
int score = x + y + z; // 计算总分
// 核心编码公式!将多个信息编码成一个整数
a[i] = (int)(score * pow(10, numbers + 2) + // 高位:总分
x * pow(10, numbers) + // 中位:语文成绩
nn - i); // 低位:学号信息
// 解释:
// pow(10, numbers+2) 创建了一个足够大的移位量
// pow(10, numbers) 创建了中间位的移位量
// nn-i 实现了学号越小,这个值越大(因为减i)
}
// 排序:按编码值从大到小排序
sort(a, a + nn, cmp); // 使用自定义的cmp函数进行降序排序
// 输出前5名(如果人数少于5,则输出全部)
for(int i = 0; i < min(5, nn); i++){
// 解码并输出
// 学号 = 1 + nn - (a[i] % 10)
// 因为编码时最后一位是 nn-i,所以 a[i] % 10 得到的就是 nn-i
// 那么 i = nn - (a[i] % 10),学号 = i + 1
cout << 1 + nn - (a[i] % 10) << " ";
// 总分 = a[i] / 10^(numbers+2) (取高位)
cout << a[i] / (int)pow(10, numbers + 2) << endl;
}
return 0;
}
五、纪念品分组



cpp
#include<iostream>
#include<algorithm> // 包含算法库,用于sort排序函数
using namespace std;
int main(){
int w, n; // w:每组纪念品的重量上限, n:纪念品件数
cin >> w >> n; // 从键盘输入w和n的值
int a[n]; // 定义数组a,存储每件纪念品的重量
// 注意:这是变长数组(VLA),不是标准C++,但很多编译器支持
// 循环输入n件纪念品的重量
for(int i = 0; i < n; i++){ // i从0到n-1
cin >> a[i]; // 输入第i件纪念品的重量
}
sort(a, a + n); // 对纪念品重量进行升序排序
// 排序后:a[0]最小,a[n-1]最大
int low = 0, high = n - 1; // 双指针:low指向最轻的,high指向最重的
int number = 0; // 计数器,记录需要的组数
// 当low指针不超过high指针时循环(即还有纪念品未分组)
while(low <= high){
// 情况1:只剩最后一件纪念品
if(low == high){
number++; // 单独成一組
break; // 结束循环
}
// 情况2:最轻的和最重的可以放在一组
if(a[low] + a[high] <= w){
number++; // 组数加1
low++; // 最轻的被分走,low指针右移
high--; // 最重的被分走,high指针左移
}
// 情况3:最轻的和最重的不能放在一组
else{
number++; // 组数加1
high--; // 最重的单独成一组,high指针左移
// low指针不动,最轻的等待与下一个最重的配对
}
}
cout << number; // 输出最少需要的组数
return 0;
}
六、买不到的数目(真题)


cpp
#include<iostream>
using namespace std;
int main(){
int x, y, result, minn, continuenumber;
// x和y:两种包装的糖果数量
// result:最终结果(最大不能组合的数)
// minn:x和y中较小的数
// continuenumber:连续可以组合的数的个数计数器
cin >> x >> y; // 输入两种包装的糖果数量
minn = min(x, y); // 取x和y中较小的数,用于判断连续区间
for(int i = 1; ; i++){
int flag = 0; // 标记当前数i是否可以由x和y组合而成,0表示不能
// 尝试用j个x包装,看剩下的能否用y包装
for(int j = 0; j * x <= i; j++){ // j从0开始,直到j*x超过i
// 如果减去j个x后,剩下的数量能被y整除
if((i - j * x) % y == 0){
flag = 1; // 标记为可以组合(找到了一个组合方式)
}
}
if(flag != 1){
result = i; // 记录当前不能组合的数
continuenumber = 0; // 连续计数器重置为0
}
else{
continuenumber++; // 可以组合,连续计数器加1
}
// 核心判断条件:如果连续可以组合的数的个数达到了minn
if(continuenumber == minn){
break; // 找到答案,退出循环
}
}
cout << result; // 输出最大不能组合的数
return 0;
}
七、数字游戏(真题)



cpp
#include<iostream>
using namespace std;
int main(){
int n, k, T;
// n: 间隔数(每隔n个数取一次)
// k: 取模的数(所有计算都对k取模)
// T: 要取的总次数
cin >> n >> k >> T; // 从键盘输入n、k、T的值
int a[T * n]; // 定义数组a,大小为T*n
// 足够存储所有需要计算的值,其实最精确且不浪费空间的为a[(T-1)×n+1]
a[0] = 1; // 初始化第一个数字为1
int result = 1; // 结果初始为1(已经取了第一个数)
// 循环计算数组的每个元素
// i从1开始,到T*n-1结束(因为数组索引从0到T*n-1)
for(int i = 1; i < T * n; i++){
// 计算当前元素的值
// 递推公式:当前值 = (前一个值 + 当前索引) % k
a[i] = (a[i-1] + i) % k;
// 判断是否是栋栋取的当前值
if(i % n == 0){
result += a[i]; // 把当前数加到结果中
}
}
cout << result;
return 0;
}
n=2, k=10, T=4 需要索引:0,2,4,6 数组大小=8,索引0-7 循环i=1-7,取i=2,4,6 → 正确 n=4, k=20, T=3 需要索引:0,4,8 数组大小=12,索引0-11 循环i=1-11,取i=4,8 → 正确
八、核桃的数量(真题)


cpp
#include<iostream>
using namespace std;
int main(){
int a,b,c;
cin>>a>>b>>c;
int maxx = max(max(a,b),c);
while(1){
if(maxx%a==0 && maxx%b==0 && maxx%c==0){
break;
}
else{
maxx++;
}
}
cout<<maxx;
return 0;
}
其实本题就是求最小公倍数
九、连号区间数


因为N个数字都不同,所以区间的最大值-最小值 = 区间长度-1 = (R-L+1)-1 = R-L
sort 需要传入地址范围,而不是元素值
应该用 sort(a+i, a+j+1) 才能排序区间 [i, j]
cpp
#include<iostream>
#include<algorithm> // 包含算法库,用于max和min函数
using namespace std;
int main(){
int N;
cin>>N; // 从键盘输入N的值
int a[N]; // 定义数组a,用于存储输入的1~N的排列
// 第一个循环:输入数组元素
for(int i=0; i<N; i++){
cin>>a[i];
}
int count = 0; // 定义计数器count,初始值为0
// 用于统计连号区间的个数
// 外层循环:枚举所有可能的区间起点
for(int i=0; i<N; i++){ // i从0到N-1,作为区间左端点
int maxVal = a[i]; // 初始化当前区间最大值为a[i]
int minVal = a[i]; // 初始化当前区间最小值为a[i]
// 内层循环:从起点i开始,枚举所有可能的区间终点
for(int j=i; j<N; j++){ // j从i到N-1,作为区间右端点
// 动态更新当前区间[i, j]的最大值和最小值
maxVal = max(maxVal, a[j]); // 将a[j]与当前最大值比较,取较大者
minVal = min(minVal, a[j]); // 将a[j]与当前最小值比较,取较小者
// 判断当前区间是否为连号区间
// 关键性质:在1~N的排列中,区间是连号区间 ⇔ 最大值-最小值 = 区间长度-1
// 区间长度 = j-i+1,所以区间长度-1 = j-i
if(maxVal - minVal == j - i){ // 如果最大值与最小值的差等于区间长度-1
count++; // 计数器加1,找到一个连号区间
}
// 如果不满足条件,说明当前区间不是连号区间,继续扩大区间
}
}
cout << count << endl;
return 0;
}
十、哈夫曼树




每次合并减少一个元素
| 循环次数 | 开始时元素个数 | 合并后元素个数 | 剩余未合并元素 |
|---|---|---|---|
| 初始状态 | n | n | n个独立元素 |
| 第1次合并 | n | n-1 | 剩下n-1个"元素"(有些是合并结果) |
| 第2次合并 | n-1 | n-2 | 剩下n-2个"元素" |
| ... | ... | ... | ... |
| 第k次合并 | n-k+1 | n-k | 剩下n-k个"元素" |
| 第n-1次合并 | 2 | 1 | 剩下1个最终结果 |
直观理解
-
一开始有 n 个数
-
每次合并操作,会把两个数变成一个数 → 总数减少1
-
要从 n 个数变成 1 个数,需要减少 n-1 次
-
所以需要 n-1 次合并
cpp
#include <iostream>
#include<algorithm> // 包含算法库,用于sort排序函数
using namespace std;
int main(){
int n, add, sum = 0;
cin >> n; // 从键盘输入学生人数n
int a[n]; // 定义长度为n的数组a,用于存储每个学生的成绩或合并后的结果
// 第一个循环:读取n个学生的初始成绩
for(int i = 0; i < n; i++) { // 循环n次,i从0到n-1
cin >> a[i]; // 输入第i个学生的成绩,存入数组a的第i个位置
}
// 第二个循环:核心合并过程,执行n-1次合并
for(int i = 0; i < n-1; i++) { // 循环n-1次(因为需要将n个数合并成1个数)
sort(a + i, a + n); // 对数组从索引i到n-1的部分进行升序排序
// 确保每次合并的都是当前最小的两个数
add = a[i] + a[i+1]; // 将当前最小的两个数相加(a[i]和a[i+1]是排序后的最小两个)
a[i+1] = add; // 将合并结果存入a[i+1]位置(覆盖原来的值)
sum += add; // 将本次合并的和累加到sum中
// 注意:a[i]的值虽然没变,但下次循环时i增加,这个值就不再被使用了
// 相当于每次合并后,有效数据范围从[i, n-1]变成了[i+1, n-1]
}
cout << sum; // 输出所有合并操作的总和
return 0;
}