1. 编程学习笔记:DHU复试Day12实战解析
今天整理的是DHU复试准备过程中的第12天学习内容,主要包含四个编程题目的详细解析和实现。这些题目涵盖了结构体应用、排序算法、字符串处理等核心编程知识点,非常适合作复试机试的练习素材。下面我会逐一拆解每个题目的解题思路和实现细节。
1.1 自定义排序与结构体应用
在开始具体题目前,先分享一个今天学到的重要知识点——sort函数自定义比较方法。这个技巧在多个题目中都有应用:
cpp复制bool Compare(const int &a, const int &b) {
return a < b; // 升序排列
// return a > b; // 降序排列
}
这个比较函数可以扩展到结构体排序中,比如我们需要按照结构体的某个特定字段排序时:
cpp复制struct Student {
string name;
int score;
};
bool CompareStudent(const Student &a, const Student &b) {
return a.score > b; // 按成绩降序
}
提示:自定义比较函数时,记住return true表示a应该排在b前面。理解这一点可以避免很多排序错误。
1.2 T112 密码分析题解
1.2.1 问题分析
题目要求统计输入字符串中各字母出现的频率(不区分大小写),并按出现次数降序输出。如果次数相同,则按字母顺序输出。
关键点在于:
- 需要统一大小写(题目要求输出大写)
- 需要记录字母和对应出现次数
- 需要按特定规则排序
1.2.2 实现方案
我采用了双结构体的设计:
- Count类:存储字符和出现次数
- mima类:核心处理类,包含字母统计数组和输出方法
cpp复制class Count {
public:
char ch;
int cnt;
Count(char c, int cnt) : ch(c), cnt(cnt) {}
Count() {}
};
class mima {
public:
int arr[26] = {0};
void show() {
vector<Count> vec;
// 将有效数据存入vector
for(int i=0; i<26; i++) {
if(arr[i] > 0) {
vec.push_back(Count('A'+i, arr[i]));
}
}
// 冒泡排序:按次数降序
for(int i=0; i<vec.size(); i++) {
for(int j=0; j<vec.size()-1; j++) {
if(vec[j].cnt < vec[j+1].cnt) {
swap(vec[j], vec[j+1]);
}
}
}
// 输出结果
for(auto &item : vec) {
cout << item.ch << " " << item.cnt << endl;
}
}
};
1.2.3 关键技巧
- 字母到数组索引的转换:
c-'A'或c-'a'将字母映射到0-25的索引 - 过滤零值:只处理出现次数大于0的字母
- 稳定排序:冒泡排序保证相同次数时字母顺序不变
注意:实际工程中建议使用STL的sort+自定义比较函数,这里用冒泡是为了练习算法思想。
1.3 T114 统计成绩题解
1.3.1 问题描述
输入5个学生的姓名、数学和政治成绩,要求:
- 输出每个学生的姓名和平均分
- 找出数学成绩最高的学生并输出其所有信息
1.3.2 实现代码
cpp复制class Student {
public:
string name;
int math, zhengzhi, average;
Student(string n, int m, int z) :
name(n), math(m), zhengzhi(z), average((m+z)/2) {}
void showBasic() {
cout << name << " " << average << endl;
}
void showAll() {
cout << name << " " << math << " " << zhengzhi << endl;
}
};
int main() {
vector<Student> students;
string name;
int math, zhengzhi;
// 输入处理
for(int i=0; i<5; i++) {
cin >> name >> math >> zhengzhi;
students.emplace_back(name, math, zhengzhi);
}
// 输出平均分
for(auto &s : students) {
s.showBasic();
}
// 找出数学最高分
int maxIndex = 0;
for(int i=1; i<students.size(); i++) {
if(students[i].math > students[maxIndex].math) {
maxIndex = i;
}
}
students[maxIndex].showAll();
return 0;
}
1.3.3 优化建议
- 使用emplace_back替代push_back避免临时对象构造
- 可以添加移动构造函数提高性能
- 数学最高分查找可以边输入边记录,减少一次遍历
1.4 T115 摩托车匹配问题
1.4.1 问题分析
客户需要特定型号的摩托车,工厂有若干库存。需要找出工厂中与客户需求匹配的所有摩托车,并按型号升序输出。
1.4.2 关键实现
cpp复制class Motorcycle {
public:
string model, color, code;
Motorcycle(string m, string c, string cd) :
model(m), color(c), code(cd) {}
void display() const {
cout << model << " " << color << " " << code << endl;
}
};
// 比较函数用于排序
bool compareModel(const Motorcycle &a, const Motorcycle &b) {
return a.model < b.model;
}
int main() {
int n, m;
while(cin >> n >> m) {
vector<string> requirements;
vector<Motorcycle> inventory;
// 读取客户需求
string req;
for(int i=0; i<n; i++) {
cin >> req;
requirements.push_back(req);
}
// 读取库存
string model, color, code;
for(int i=0; i<m; i++) {
cin >> model >> color >> code;
inventory.emplace_back(model, color, code);
}
// 按型号排序
sort(inventory.begin(), inventory.end(), compareModel);
// 查找匹配
bool found = false;
for(const auto &req : requirements) {
for(const auto &bike : inventory) {
if(bike.model == req) {
bike.display();
found = true;
}
}
}
if(!found) cout << "Not found!" << endl;
}
return 0;
}
1.4.3 性能优化
- 可以使用unordered_map建立型号到摩托车的映射,将O(n²)复杂度降为O(n)
- 可以先对客户需求也进行排序,然后使用双指针法进一步优化
1.5 T117 混合牛奶问题
1.5.1 问题重述
商人需要购买N单位牛奶,有M个农民提供不同价格和数量的牛奶。目标是以最小成本购买足够的牛奶。
1.5.2 贪心算法应用
这个问题是典型的贪心算法应用场景:
- 优先购买单价最低的牛奶
- 直到买够所需数量
cpp复制struct Farmer {
long long price;
long long amount;
Farmer(long long p, long long a) : price(p), amount(a) {}
};
bool comparePrice(const Farmer &a, const Farmer &b) {
return a.price < b.price;
}
int main() {
long long needed, farmerCount;
cin >> needed >> farmerCount;
vector<Farmer> farmers;
long long price, amount;
for(long long i=0; i<farmerCount; i++) {
cin >> price >> amount;
farmers.emplace_back(price, amount);
}
// 按价格排序
sort(farmers.begin(), farmers.end(), comparePrice);
long long totalCost = 0;
long long remaining = needed;
for(const auto &f : farmers) {
if(remaining <= 0) break;
long long buy = min(remaining, f.amount);
totalCost += buy * f.price;
remaining -= buy;
}
cout << totalCost << endl;
return 0;
}
1.5.3 算法分析
- 时间复杂度:O(MlogM)来自排序,后续处理是O(M)
- 空间复杂度:O(M)存储农民信息
- 正确性证明:贪心选择性质成立,因为局部最优解(买当前最便宜的)能导致全局最优解
注意事项:这里使用long long是为了防止大数计算时的溢出问题,在实际编程比赛中,数据范围是需要特别关注的细节。
1.6 学习心得与技巧分享
通过这天的练习,有几个重要的编程经验值得分享:
-
结构体设计原则:
- 将与同一实体相关的数据封装在一起
- 提供必要的构造函数简化对象创建
- 将与数据相关的操作也封装在结构体中
-
排序技巧:
- 熟练掌握STL的sort算法
- 理解自定义比较函数的编写方法
- 记住稳定排序和不稳定排序的区别
-
输入处理:
- 使用getline处理可能包含空格的输入
- 对于固定数量的输入,可以预先分配vector空间
- 注意输入结束条件的判断(如while(cin>>n))
-
算法选择:
- 理解贪心算法的适用场景
- 对于查找问题,先考虑排序是否能简化问题
- 在时间复杂度允许的情况下,选择最直观的解法
在实际练习中,我发现理解题目要求是最关键的一步。比如混合牛奶问题,刚开始没有理解"按需购买"的含义,导致解题思路出现偏差。建议大家在动手编码前,先用自己的话复述题目要求,确保理解正确。