题解:洛谷 P1223 排队接水
【题目来源】
洛谷:P1223 排队接水 - 洛谷(luogu.com.cn)
【题目描述】
有 \(n\) 个人在一个水龙头前排队接水,假如每个人接水的时间为 \(T_i\),请编程找出这 \(n\) 个人排队的一种顺序,使得 \(n\) 个人的平均等待时间最小。
【输入】
第一行为一个整数 \(n\)。
第二行 \(n\) 个整数,第 \(i\) 个整数 \(T_i\) 表示第 \(i\) 个人的等待时间 \(T_i\)。
【输出】
输出文件有两行,第一行为一种平均时间最短的排队顺序;第二行为这种排列方案下的平均等待时间(输出结果精确到小数点后两位)。
【输入样例】
10
56 12 1 99 1000 234 33 55 99 812
【输出样例】
3 2 7 8 1 4 9 6 10 5
291.90
【核心思想】
-
问题分析:有 \(n\) 个人排队接水,每个人接水所需时间 \(T_i\) 不同。若某个人在第 \(i\) 位接水,则他之后的所有人(共 \(n-i\) 人)都会等待他完成,产生额外的等待时间。总等待时间等于每个人接水时间乘以其后面人数的总和。目标是找到一个排队顺序,使总等待时间(或平均等待时间)最小。
-
算法选择:
- 贪心策略(最短作业优先):将接水时间短的人排在前面。对于任意相邻两人,若前者时间大于后者,交换两者可使总等待时间减少,因为前者时间对后续的贡献更大。因此最优顺序为按接水时间从小到大排序。
- 排序:使用结构体存储每个人的时间和原始编号,按时间升序排序。
- 计算总等待时间:排序后,第 \(i\) 个人的时间 \(T_i\) 会对后面 \(n-i\) 个人产生等待,总等待时间 \(sum = \sum_{i=1}^{n} T_i \times (n-i)\),平均等待时间 \(= sum / n\)。
-
关键步骤:
- 读入 \(n\) 和每个 \(T_i\),记录原始编号(因为输出需要按顺序输出人的编号)。
- 按 \(T_i\) 升序排序(若时间相同,编号顺序无关,但通常按输入顺序或任意)。
- 输出排序后的编号序列,作为最优排队顺序。
- 计算总等待时间 \(sum = \sum_{i=1}^{n} T_i \times (n-i)\)。
- 输出平均等待时间 \(sum / n\),保留两位小数。
-
时间/空间复杂度:
- 时间复杂度:\(O(n \log n)\),主要来自排序。
- 空间复杂度:\(O(n)\),存储结构体数组。
-
贪心策略的核心思想:
- 交换论证:若存在相邻两人 \(i\) 在前、\(j\) 在后,且 \(T_i > T_j\),交换它们后,其他所有人的等待时间不变,而 \(i\) 和 \(j\) 的等待时间变化量为 \(T_i - T_j\)(原来 \(i\) 影响 \(j\),交换后 \(j\) 影响 \(i\)),总等待时间减少 \(T_i - T_j > 0\)。因此,任何逆序都会降低总时间,故最优顺序为升序。
- 局部最优到全局最优:每次选择当前剩余人中时间最短的放在最前面,该贪心选择不会使后续最优解变差。
- 平均等待时间:总等待时间除以人数即为平均等待时间,两者同序优化。
- 适用于单服务器、作业处理时间固定的调度问题,是经典的“最短处理时间优先”调度策略。
【解题思路】

【算法标签】
普及- #贪心
【代码详解】
#include <bits/stdc++.h> // 包含所有标准库头文件
using namespace std;
// 定义人员结构体
struct people
{
int time; // 该人员完成任务所需时间
int num; // 人员编号
} ps[1005]; // 最多1005人
// 自定义排序比较函数:按完成任务时间升序排序
bool cmp(people p1, people p2)
{
return p1.time < p2.time;
}
int main()
{
int n; // 总人数
double sum = 0; // 总等待时间
double avg; // 平均等待时间
// 输入总人数
cin >> n;
// 输入每个人的任务完成时间,并记录人员编号
for (int i = 1; i <= n; i++)
{
cin >> ps[i].time;
ps[i].num = i;
}
// 按任务完成时间从短到长排序
sort(ps + 1, ps + n + 1, cmp);
// 输出最优排队顺序
for (int i = 1; i <= n; i++)
{
cout << ps[i].num << " ";
// 计算该人员后面所有人的等待时间
sum += (n - i) * ps[i].time;
}
cout << endl;
// 计算并输出平均等待时间(保留两位小数)
avg = sum / n;
printf("%.2f", avg);
return 0;
}
#include <bits/stdc++.h>
using namespace std;
struct Node
{
int t, id; // 接水时间,编号
bool operator < (Node &b) // 按接水时间从小到大排序
{
return t < b.t;
}
} a[1010];
int main()
{
int n;
cin >> n; // 输入人数
for (int i = 1; i <= n; i++) // 输入每个人的接水时间
{
cin >> a[i].t;
a[i].id = i; // 记录原始编号
}
sort(a + 1, a + n + 1); // 按接水时间排序
for (int i = 1; i <= n; i++) // 输出最优顺序
{
cout << a[i].id << " ";
}
cout << endl;
double tim = 0; // 总等待时间
for (int i = 1; i <= n; i++) // 计算平均等待时间
{
tim += a[i].t * (n - i); // 第i个人的等待时间会影响后面n-i个人
}
printf("%.2lf", tim / n); // 输出平均等待时间
return 0;
}
【运行结果】
10
56 12 1 99 1000 234 33 55 99 812
3 2 7 8 1 4 9 6 10 5
291.90
浙公网安备 33010602011771号