OneCoder Avatar
OneCodercoderli.com · 958 篇博文
四级

C++ 算法考级专栏

真题分析、矩阵探测、递归回溯与基础语法

🎨 视觉封面

【GESP】C++四级/五级练习题 luogu-P1223 排队接水

📅 2025-12-18·✍️ OneCoder·计算中...·⏱️ 6 分钟
#GESP#C++#排序#贪心

GESP C++ 四级/五级练习题,贪心思想思想考点。题目难度⭐⭐★☆☆,适合五级入门和四级练习,洛谷难度等级普及-

luogu-P1223 排队接水

题目要求

题目描述

nn 个人在一个水龙头前排队接水,假如每个人接水的时间为 TiT_i,请编程找出这 nn 个人排队的一种顺序,使得 nn 个人的平均等待时间最小。

一个人的等待时间不包括他的接水时间。

如果两个人接水的时间相同,编号更小的人应当排在前面。

输入格式

第一行为一个整数 nn

第二行 nn 个整数,第 ii 个整数 TiT_i 表示第 ii 个人的接水时间 TiT_i

输出格式

输出文件有两行,第一行为一种平均时间最短的排队顺序;第二行为这种排列方案下的平均等待时间(输出结果精确到小数点后两位)。

输入输出样例 #1

输入 #1
PLAINTEXT 2 行
10
56 12 1 99 1000 234 33 55 99 812
输出 #1
PLAINTEXT 2 行
3 2 7 8 1 4 9 6 10 5
291.90

说明/提示

1n10001\le n \leq 10001ti1061\le t_i \leq 10^6,不保证 tit_i 不重复。


题目分析

这是一道典型的贪心算法题目。

1. 问题分析

题目要求使 nn 个人的平均等待时间最小。因为 nn 是固定的,要使平均值最小,实际上就是要使总等待时间最小。

假设 nn 个人按某个顺序排队,接水时间分别为 t1,t2,,tnt_1, t_2, \dots, t_n

  • 第 1 个人接水时,后面 n1n-1 个人在等待,耗时 t1×(n1)t_1 \times (n-1)
  • 第 2 个人接水时,后面 n2n-2 个人在等待,耗时 t2×(n2)t_2 \times (n-2)
  • ...
  • ii 个人接水时,后面 nin-i 个人在等待,耗时 ti×(ni)t_i \times (n-i)

总等待时间 S=i=1nti×(ni)S = \sum_{i=1}^{n} t_i \times (n-i)

2. 贪心策略

观察公式可以发现,越靠前的人,其接水时间 tit_i 被计算的次数越多(系数 nin-i 越大)。为了让总和 SS 最小,我们需要让耗时短的人排在前面,耗时长的人排在后面。即:按接水时间从小到大排序

3. 细节处理

  • 排序规则:题目要求“如果两个人接水的时间相同,编号更小的人应当排在前面”。因此在排序比较函数中,除了比较时间 time,还要处理 time 相等时比较 id 的情况。
  • 数据范围n1000,ti106n \le 1000, t_i \le 10^6。总等待时间可能会达到 1000×1000×106/25×10111000 \times 1000 \times 10^6 / 2 \approx 5 \times 10^{11},超过了 int 的表示范围(约 2×1092 \times 10^9),因此累加变量 sum 必须使用 long long 类型。
  • 输出格式:平均等待时间需要保留两位小数,可以使用 printf("%.2f", ...)fixed + setprecision(2)

示例代码

C++ 43 行
#include <algorithm>
#include <iomanip>
#include <iostream>

typedef long long ll;
// 定义结构体Water,用于存储每个人的编号和接水时间
struct Water {
    int id;    // 人的编号
    int time;  // 接水所需时间
};

// 比较函数:按照接水时间从小到大排序
// 如果接水时间相同,则按照编号从小到大排序
bool cmp(Water a, Water b) {
    if (a.time == b.time) {
        return a.id < b.id;
    }
    return a.time < b.time;
}

struct Water t[1005];  // 数组大小大于1000,符合题目范围
int main() {
    int n;
    std::cin >> n;
    for (int i = 1; i <= n; i++) {
        t[i].id = i;            // 记录初始编号
        std::cin >> t[i].time;  // 输入接水时间
    }
    // 贪心策略:接水时间越短的人排在越前面,能使后续所有人的等待时间总和最小
    std::sort(t + 1, t + n + 1, cmp);
    ll sum = 0;
    for (int i = 1; i <= n; i++) {
        std::cout << t[i].id << " ";
        // 计算总等待时间:
        // 第i个人接水时,排在他后面的 n-i 个人都在等待
        // 因此第i个人的时间贡献了 (n-i) 次等待
        sum += t[i].time * (n - i);
    }
    std::cout << "\n";
    // 输出平均等待时间,保留两位小数
    std::cout << std::setprecision(2) << std::fixed << (double)sum / n;
    return 0;
}

💡 OneCoder 资源指引

所有代码开源上传至 GitHub:yummy-code 仓库 · GESP 专题站:GESP WIKI

🤝 技术交流与答疑

欢迎加入:C++ GESP/CSP 考级答疑群(688906745)Java/Python交流群(982860385),点击可直接加群。

📚

猜你想读 · 相关文章推荐

OneCoder

OneCoder (lihongzheshuai)

一个中年人的自留地,记录学习 C++、GESP/NOI、Java、Python 与算法架构的心得体会。本站唯一网址:coderli.com

读者讨论与留言

0 条讨论
✨ 支持点击留言直接回复 · Markdown 引用格式
💬 还没有读者留言,快来成为第一个讨论者吧!