题解:洛谷 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

【核心思想】

  1. 问题分析:有 \(N\) 名学生,每名学生有姓名和三科成绩。需要找出所有满足条件的“旗鼓相当的对手”组合:每科成绩分差都不大于 5,且总分分差不大于 10。输出时要求按字典序(姓名字典序)排序,且每对组合中第一个名字的字典序小于第二个。本质是一个结构体数组 + 双重循环枚举 + 多重条件判断的问题。

  2. 算法选择

    • 结构体存储:使用结构体存储姓名、三科成绩和总分。
    • 双重循环枚举:枚举所有 \(i < j\) 的组合,检查四个条件(三科分差 \(\le 5\) 且总分分差 \(\le 10\))。
    • 字典序处理:由于姓名是按字典序输入的,且双重循环按顺序枚举,输出时自然满足题目要求的字典序顺序,无需额外排序。
    • 复杂度:时间复杂度 \(O(N^2)\)\(N \le 1000\),完全可行。
  3. 关键步骤

    • 读入与存储:读取 \(N\),对每个学生读入姓名和三科成绩,计算总分并存入结构体。
    • 枚举组合:外层循环 \(i\) 从 1 到 \(N\),内层循环 \(j\)\(i+1\)\(N\)
    • 条件判断
      • abs(yu[i] - yu[j]) <= 5
      • abs(shu[i] - shu[j]) <= 5
      • abs(ying[i] - ying[j]) <= 5
      • abs(zong[i] - zong[j]) <= 10
    • 输出:若满足所有条件,输出 name[i] + " " + name[j]
  4. 时间/空间复杂度

    • 时间复杂度:\(O(N^2)\)\(N \le 1000\),约 \(5 \times 10^5\) 次判断,可接受。
    • 空间复杂度:\(O(N)\),存储学生信息。
  5. 结构体与枚举

    • 结构体复用:与 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
posted @ 2026-09-01 09:26  团爸讲算法  阅读(15)  评论(0)    收藏  举报