【深基5.例5】旗鼓相当的对手

GitHub跳转原题关系图返回列表

枚举所有学生对,检查三科分差都不超过 5 且总分差不超过 10。

OJ: luogu

题目 ID: P5728

难度:入门

标签:枚举模拟列表python

日期: 2026-07-15 18:44

题意

给出 N 名同学的语文、数学、英语成绩。若一对同学满足:

  • 每一科成绩差都不超过 5
  • 总分差不超过 10

则这对同学是“旗鼓相当的对手”。求这样的学生对数量。

思路

N <= 1000,枚举所有无序学生对 i < j 即可。

为了检查总分差,读入每个学生时顺手计算总分,并把四个数存成:

text
(语文, 数学, 英语, 总分)

枚举一对学生时,先检查三科分差。如果有一科超过 5,这一对不合法。三科都满足后,再检查总分差是否不超过 10

这题的正解就是按定义枚举所有学生对,不创建额外 brute.py

Python 知识

  • /home/rainboy/mycode/hugo-blog/content/program_language/python/input_output_and_strings.md:多行三整数输入使用 map(int, input().split())
  • /home/rainboy/mycode/hugo-blog/content/program_language/python/brute_force_validation.mdfor i in range(n)for j in range(i + 1, n) 枚举无序点对。
  • 元组 (chinese, math, english, total) 可以把一个学生的信息打包保存。
  • abs(a - b) 计算分差的绝对值。

代码

python
n = int(input())
students = []

for _ in range(n):
    chinese, math, english = map(int, input().split())
    total = chinese + math + english
    students.append((chinese, math, english, total))

answer = 0

for i in range(n):
    for j in range(i + 1, n):
        ok = True
        for subject in range(3):
            if abs(students[i][subject] - students[j][subject]) > 5:
                ok = False
                break
        if ok and abs(students[i][3] - students[j][3]) <= 10:
            answer += 1

print(answer)
cpp
/**
 * Author by Rainboy blog: https://rainboylv.com github: https://github.com/rainboylvx
 * rbook: -> https://rbook.roj.ac.cn  https://rbook2.roj.ac.cn
 * rainboy的学习导航网站: https://idx.roj.ac.cn
 * create_at: 2026-07-27 00:00
 * update_at: 2026-07-27 00:00
 */

#include <bits/stdc++.h>
using namespace std;

int ch[1005]; // 语文成绩
int ma[1005]; // 数学成绩
int en[1005]; // 英语成绩
int tot[1005]; // 总分
int n, ans;

int main() {
    cin >> n;
    for (int i = 1; i <= n; i++) {
        cin >> ch[i] >> ma[i] >> en[i];
        tot[i] = ch[i] + ma[i] + en[i];
    }
    // 枚举所有学生对 i < j
    for (int i = 1; i <= n; i++) {
        for (int j = i + 1; j <= n; j++) {
            // 三科分差都不超过 5,且总分差不超过 10
            if (abs(ch[i] - ch[j]) <= 5 &&
                abs(ma[i] - ma[j]) <= 5 &&
                abs(en[i] - en[j]) <= 5 &&
                abs(tot[i] - tot[j]) <= 10) {
                ans++;
            }
        }
    }
    cout << ans;
    return 0;
}

Pythonic 写法

Python 没有 Haskell 的 $ / 管道运算符,可以用轻量 pipe 把上一步结果传给下一步:

  1. students 读入成绩;
  2. combinations(..., 2) 生成所有学生对;
  3. sum(is_opponent(...)) 统计满足条件的对数。

判断条件单独写成 is_opponent,避免括号嵌套:

python
from itertools import combinations


def pipe(x, *fs):
    for f in fs:
        x = f(x)
    return x


def is_opponent(x, y):
    return all(abs(a - b) <= 5 for a, b in zip(x, y)) and abs(sum(x) - sum(y)) <= 10


n = int(input())
students = [tuple(map(int, input().split())) for _ in range(n)]
answer = pipe(
    students,
    lambda xs: combinations(xs, 2),
    lambda pairs: sum(is_opponent(x, y) for x, y in pairs),
)
print(answer)

复杂度

共有 O(n2)O(n^2) 对学生,每对检查常数个成绩,时间复杂度是 O(n2)O(n^2);存储学生信息需要 O(n)O(n) 空间。

总结

这题的关键是枚举“不重复的一对学生”,也就是只枚举 i < j。每对学生按题面条件逐项检查即可。