题目描述
有 n个人在一个水龙头前排队接水,假如每个人接水的时间为 Ti,请编程找出这 n 个人排队的一种顺序,使得 n 个人的平均等待时间最小。
输入格式
第一行为一个整数 n。
第二行 n 个整数,第 i个整数 Tii 表示第 ii 个人的接水时间 Ti。
输出格式
输出文件有两行,第一行为一种平均时间最短的排队顺序;第二行为这种排列方案下的平均等待时间(输出结果精确到小数点后两位)。
输入输出样例
输入 #1复制
10 56 12 1 99 1000 234 33 55 99 812
输出 #1复制
3 2 7 8 1 4 9 6 10 5 291.90
说明/提示
1≤n≤10001≤n≤1000,不保证 ti不重复。
解题思路
- 问题理解:
- 给定n个人和每个人接水的时间Ti,需要找到一种排队顺序,使得所有人的平均等待时间最小。
- 平均等待时间是指每个人等待的总时间(包括自己接水的时间)除以人数。
- 优化策略:
- 最优的排队策略是让接水时间短的人先接水,这样后面等待的人会因为前面的人接水时间短而减少等待时间。
- 这是一个典型的贪心算法应用场景,因为局部最优(即当前接水时间最短的人先接水)可以推导出全局最优(即所有人的平均等待时间最短)。
- 实现步骤:
- 首先,读取n和每个人的接水时间Ti。
- 将每个人的接水时间和其在输入中的顺序(即排队顺序的候选)存储在一个结构体数组中。
- 对这个数组按照接水时间进行排序,排序后的顺序即为最优排队顺序。
- 根据排序后的顺序计算每个人的等待时间,并累加得到总等待时间。
- 输出最优排队顺序和平均等待时间(保留两位小数)。
解题思路
#include<bits/stdc++.h>
using namespace std;
// 定义结构体,存储接水时间和原始顺序
struct j {
int sum, shunxu;
} pai[1001]; // 数组大小设为1001以防n=1000时越界
// 自定义比较函数,用于按接水时间排序
bool cmp(j x, j y) {
return x.sum < y.sum;
}
int main() {
int n, i;
double SUM = 0;
cin >> n;
// 读取每个人的接水时间和原始顺序
for (i = 1; i <= n; i++) {
cin >> pai[i].sum;
pai[i].shunxu = i;
}
// 对结构体数组按照接水时间进行排序
sort(pai + 1, pai + n + 1, cmp);
// 输出最优排队顺序
for (i = 1; i <= n; i++) {
cout << pai[i].shunxu << " ";
}
cout << endl;
// 计算总等待时间
// 注意:这里的X代表当前等待的人数,即从后往前数第X个人还在等待
// 因为是第i个人接水,所以后面有n-i个人还在等待,这些人都会增加i*pai[i].sum的等待时间
for (int X = n - 1; X >= 0; X--) { // 注意这里应该是X>=0,因为最后一个人接水时,没有人在等待
SUM += (double)pai[n - X].sum * (X + 1); // 累加每个人的等待时间
}
// 输出平均等待时间,保留两位小数
printf("%.2lf", SUM / n);
return 0;
}