题解:洛谷 P5741 旗鼓相当的对手(加强版)
【题目来源】
洛谷:P5741 【深基7.例10】旗鼓相当的对手 - 加强版 - 洛谷(luogu.com.cn)
【题目描述】
现有 \(N(N\le 1000)\) 名同学参加了期末考试,并且获得了每名同学的信息:姓名(不超过 \(8\) 个字符的字符串,没有空格)、语文、数学、英语成绩(均为不超过 \(150\) 的自然数)。如果某对学生 <\(i,j\)> 的每一科成绩的分差都不大于 \(5\),且总分分差不大于 \(10\),那么这对学生就是“旗鼓相当的对手”。现在我们想知道这些同学中,哪些是“旗鼓相当的对手”?请输出他们的姓名。
所有人的姓名是按照字典序给出的,输出时也应该按照字典序输出所有对手组合。也就是说,这对组合的第一个名字的字典序应该小于第二个;如果两个组合中第一个名字不一样,则第一个名字字典序小的先输出;如果两个组合的第一个名字一样但第二个名字不同,则第二个名字字典序小的先输出。
【输入】
第一行输入一个正整数 \(N\),表示学生个数。
第二行开始,往下 \(N\) 行,对于每一行首先先输入一个字符串表示学生姓名,再输入三个自然数表示语文、数学、英语的成绩。均用空格相隔。
【输出】
输出若干行,每行两个以空格隔开的字符串,表示一组旗鼓相当的对手。注意题目描述中的输出格式。
【输入样例】
3
fafa 90 90 90
lxl 95 85 90
senpai 100 80 91
【输出样例】
fafa lxl
lxl senpai
【核心思想】
-
问题分析:有 \(N\) 名学生,每名学生有姓名和三科成绩。需要找出所有满足条件的“旗鼓相当的对手”组合:每科成绩分差都不大于 5,且总分分差不大于 10。输出时要求按字典序(姓名字典序)排序,且每对组合中第一个名字的字典序小于第二个。本质是一个结构体数组 + 双重循环枚举 + 多重条件判断的问题。
-
算法选择:
- 结构体存储:使用结构体存储姓名、三科成绩和总分。
- 双重循环枚举:枚举所有 \(i < j\) 的组合,检查四个条件(三科分差 \(\le 5\) 且总分分差 \(\le 10\))。
- 字典序处理:由于姓名是按字典序输入的,且双重循环按顺序枚举,输出时自然满足题目要求的字典序顺序,无需额外排序。
- 复杂度:时间复杂度 \(O(N^2)\),\(N \le 1000\),完全可行。
-
关键步骤:
- 读入与存储:读取 \(N\),对每个学生读入姓名和三科成绩,计算总分并存入结构体。
- 枚举组合:外层循环 \(i\) 从 1 到 \(N\),内层循环 \(j\) 从 \(i+1\) 到 \(N\)。
- 条件判断:
abs(yu[i] - yu[j]) <= 5abs(shu[i] - shu[j]) <= 5abs(ying[i] - ying[j]) <= 5abs(zong[i] - zong[j]) <= 10
- 输出:若满足所有条件,输出
name[i] + " " + name[j]。
-
时间/空间复杂度:
- 时间复杂度:\(O(N^2)\),\(N \le 1000\),约 \(5 \times 10^5\) 次判断,可接受。
- 空间复杂度:\(O(N)\),存储学生信息。
-
结构体与枚举:
- 结构体复用:与 P5740 类似,结构体使代码更清晰。
- 多条件判断:使用逻辑与(
&&)连接所有条件,确保同时满足。 - 字典序输出:由于姓名按字典序输入,且枚举顺序 \(i < j\) 保证了第一个名字的字典序小于第二个,同时外层循环按 \(i\) 递增,内层按 \(j\) 递增,输出顺序自然符合要求。
- 适用场景:这类“多条件匹配”问题,在数据规模不大时,双重循环是直接且可靠的方法。
【算法标签】
普及- #模拟
【代码详解】
#include <bits/stdc++.h> // 包含标准库头文件(万能头文件)
using namespace std; // 使用标准命名空间
// 定义学生结构体
struct student
{
string name; // 学生姓名
int yu; // 语文成绩
int shu; // 数学成绩
int ying; // 英语成绩
int zong; // 总成绩
};
int main()
{
student s[1005]; // 定义学生数组,最多存储1005个学生
int n; // 定义变量:学生数量
cin >> n; // 输入学生数量
// 输入每个学生的信息并计算总成绩
for (int i = 1; i <= n; i++)
{
cin >> s[i].name >> s[i].yu >> s[i].shu >> s[i].ying; // 输入学生信息
s[i].zong = s[i].yu + s[i].shu + s[i].ying; // 计算总成绩
}
// 比较每对学生,找出成绩相近的组合
for (int i = 1; i <= n; i++)
{
for (int j = i + 1; j <= n; j++)
{
// 检查各科成绩和总成绩的差值是否满足条件
if (abs(s[i].yu - s[j].yu) <= 5 &&
abs(s[i].shu - s[j].shu) <= 5 &&
abs(s[i].ying - s[j].ying) <= 5 &&
abs(s[i].zong - s[j].zong) <= 10)
{
// 输出符合条件的配对
cout << s[i].name << " " << s[j].name << endl;
}
}
}
return 0; // 程序正常结束
}
【运行结果】
3
fafa 90 90 90
lxl 95 85 90
senpai 100 80 91
fafa lxl
lxl senpai
浙公网安备 33010602011771号