算法日记 | C++ 结构体实战:如何优雅地处理"复杂数据"?
👋 大家好。
今天我们来点轻松但同样重要的基础内功------ 结构体 (Struct) 。
很多同学写代码时,还在用 a[100], b[100], c[100] 这样散乱的数组来存数据吗?一旦数据多了,是不是经常搞混哪个数组对应哪个属性?🤯
别慌!今天我们就通过三道洛谷的经典题目(P5740、P5744、P5741),手把手教你如何用 struct 把数据"打包"管理,让代码瞬间变得整洁又强大!🚀
准备好了吗?让我们开始今天的"打包"之旅吧!🎒
🌟 关卡一:寻找"卷王"之王
首先登场的是我们的热身题:P5740【深基 7.例 9】最厉害的学生。
🤔 题目大意
班里有 N 个同学,每个人都有姓名、语文、数学、英语成绩。我们需要找出总分最高的那个"学霸"。如果有并列第一,就选名单里排在最前面的那个。
💡 为什么用结构体?
想象一下,如果你不用结构体,你可能需要开四个数组:
-
string names[1000] -
int chinese[1000] -
...
如果要排序,你得动其中一个数组,其他三个也得跟着换位置,简直是灾难现场!💥 而有了结构体,我们只需要把它们"打包"在一起。
💻 核心代码解析
cpp
// 定义结构体,把相关数据绑在一起
struct student{
string name;
int chinese, math, english;
int score; // 总分
};
// 自定义比较函数:按分数从高到低排
bool f(const student& a, const student& b){
return a.score > b.score;
}
int main(){
vector<student> s;
int n;
cin >> n;
while(n--){
student temp;
cin >> temp.name >> temp.chinese >> temp.math >> temp.english;
temp.score = temp.chinese + temp.math + temp.english; // 算出总分
s.push_back(temp);
}
// 关键点:使用 stable_sort 保持相对顺序
stable_sort(s.begin(), s.end(), f);
// 输出第一名
cout << s.begin()->name << " "
<< s.begin()->chinese << " "
<< s.begin()->math << " "
<< s.begin()->english << endl;
return0;
}
🧐 避坑指南
注意看这里我用的是 stable_sort 而不是普通的 sort。
题目要求:"如果总分相同,输出靠前的那位"。普通的 sort 可能会打乱原本输入的顺序,而 stable_sort 能保证在分数相同时,先输入的依然排在前面。这就是细节决定成败!😎
🌟 关卡二:培训机构的"成长"记录
搞定了排序,我们来看看数据的动态变化。第二题是 P5744【深基 7.习 9】培训。
🤔 题目大意
有一批学员参加培训,经过一年后:
-
每个人都长了一岁(年龄 +1)。
-
NOIP 成绩提高了 20%(但是满分 600,不能超过哦)。
我们需要输入原来的信息,输出培训后的新信息。
💡 核心考点:结构体的遍历与计算
这道题不需要复杂的排序,重点在于如何优雅地处理每一个学生的数据更新。看看我的解法,我使用了迭代器(Iterator)来遍历整个班级:
cpp
for(vector<student>::iterator it = s.begin(); it != s.end(); it++){
// 姓名不变,直接输出
cout << it->name << " ";
// 年龄自动加 1
cout << it->year + 1 << " ";
// 成绩提升 20%,但要注意上限 600
if(it->score * 1.2 > 600)
cout << 600 << endl;
else
cout << it->score * 1.2 << endl;
}
⚠️ 细节决定成败
这里有个小坑!题目说了成绩不能超过 600。虽然乘以 1.2 很简单,但我们必须加上一个 if 判断来进行"截断"。编程不仅要算得对,还要符合现实世界的规则!
🌟 关卡三:谁是"旗鼓相当"的对手?
最后一题稍微有点难度:P5741【深基 7.例 10】旗鼓相当的对手 - 加强版。
🤔 题目大意
如果有两个学生,他们的语文、数学、英语分差都不超过 5 分,且总分差不超过 10 分,那他们就是"旗鼓相当的对手"。请找出所有这样的组合。
💡 核心思路:双重循环暴力匹配
这道题的数据范围N<=1000 ,我们可以放心地使用双重循环来两两比较。
cpp
// 判断是否为对手的函数
bool isRival(const student& s1, const student& s2){
if(abs(s1.a - s2.a) <= 5 &&
abs(s1.b - s2.b) <= 5 &&
abs(s1.c - s2.c) <= 5 &&
abs(s1.score - s2.score) <= 10)
returntrue;
else
returnfalse;
}
int main(){
// ... 输入部分省略 ...
// 双重循环查找
for(int i = 0; i < s.size(); i++){
for(int j = i + 1; j < s.size(); j++){
// 只比较后面的,避免重复输出 (A,B) 和 (B,A)
if(isRival(s[i], s[j])){
cout << s[i].name << " " << s[j].name << endl;
}
}
}
return0;
}
🔍 为什么这样写?
-
封装判断逻辑 :我把判断条件写在了
isRival函数里,主函数看起来就非常清爽。 -
去重技巧 :内层循环从
j = i + 1开始。这意味着我们只拿第 个人和他后面的人比。这样既不会自己和自己比,也不会出现"A和B是对手,B和A也是对手"的重复情况。
📝 总结一下
今天我们通过三道题,掌握了结构体的三大用法:
-
P5740 :利用结构体进行整体排序 (记得
stable_sort)。 -
P5744 :利用结构体进行批量数据处理(遍历修改)。
-
P5741 :利用结构体进行对象间的关系匹配(双重循环)。
如果你觉得这篇文章对你有帮助,欢迎点赞、在看、转发三连支持一下!你的鼓励是我更新的最大动力!❤️