acwing 913. 排队打水
有 n n n 个人排队到 1 个水龙头处打水,第 i i i 个人装满水桶所需的时间是 t i t_i ti,请问如何安排他们的打水顺序才能使所有人的等待时间之和最小?
输入格式
第一行包含整数 n n n。
第二行包含 n n n 个整数,其中第 i i i个整数表示第 i i i 个人装满水桶所花费的时间 t i t_i ti。
输出格式
输出一个整数,表示最小的等待时间之和。
数据范围
1 ≤ n ≤ 1 0 5 1 \leq n \leq 10^5 1≤n≤105,
1 ≤ t i ≤ 1 0 4 1 \leq t_i \leq 10^4 1≤ti≤104
输入样例:
7
3 6 1 4 2 5 7
输出样例:
56
思考过程/解题步骤:
这题考察的是一个贪心的思想,需要我们如何选取来达到最优的等待时间即最短的等待时间
-
第一步:将所有的时间按照从小到大排序
-
第二步:考虑到第 i i i个人装水的时间是 t i t_i ti,那么后面 n − i n-i n−i人都需要等待 t i t_i ti的时间,即总的等待时间 T i T_i Ti为
T i = t i ∗ ( n − i ) T_i = t_i * (n-i) Ti=ti∗(n−i) (如果下标 i i i从0开始,那么括号里面的就需要改成 ( n − i − 1 ) (n-i-1) (n−i−1))
下面给出这种贪心猜测的证明:
反证法:
如果说所有的时间序列不是按照从小到大递增有序的,那必然可以找到一组相邻的等待时间 t i 和 t i + 1 t_i 和 t_{i+1} ti和ti+1 数字满足 t i > t i + 1 t_i > t_{i+1} ti>ti+1这种关系。那么将这两个数对调变成了递增有序的之后,就会是 t i + 1 t_{i+1} ti+1在 t i t_i ti之前,因此有序之后等待 t i + 1 t_{i+1} ti+1时间的是 n − i n-i n−i个人,等待 t i t_i ti时间的是 n − i − 1 n-i-1 n−i−1个人。我们假设一组数据中仅仅存在两个等待时间 t i t_i ti数字不是按照从小到大排序,其他的都是按照从小到大排序的。
那么,将非递增序列这种情况下的时间总数 N i N_i Ni与递增的有序序列时间总数 M i M_i Mi对比,
N i = t 1 ∗ ( n − 1 ) + t 2 ∗ ( n − 2 ) + . . . + t i ∗ ( n − i ) + t i + 1 ∗ ( n − i − 1 ) + . . . + t n − 1 ∗ ( n − ( n − 1 ) ) + t n ∗ ( n − n ) N_i = t_1*(n-1) + t_2*(n-2) +...+t_i*(n-i) +t_{i+1}*(n-i-1) +...+t_{n-1}*(n-(n-1)) +t_n * (n - n) Ni=t1∗(n−1)+t2∗(n−2)+...+ti∗(n−i)+ti+1∗(n−i−1)+...+tn−1∗(n−(n−1))+tn∗(n−n)
M i = t 1 ∗ ( n − 1 ) + t 2 ∗ ( n − 2 ) + . . . + t i ∗ ( n − i − 1 ) + t i + 1 ∗ ( n − i ) + . . . + t n − 1 ∗ ( n − ( n − 1 ) ) + t n ∗ ( n − n ) M_i=t_1*(n-1) + t_2*(n-2) +...+t_i*(n-i-1)+t_{i+1}*(n-i) +...+t_{n-1}*(n-(n-1)) +t_n * (n - n) Mi=t1∗(n−1)+t2∗(n−2)+...+ti∗(n−i−1)+ti+1∗(n−i)+...+tn−1∗(n−(n−1))+tn∗(n−n)
因此, N i − M i = t i − t i + 1 N_i - M_i = t_i - t_{i+1} Ni−Mi=ti−ti+1,又因为这个前提是 t i > t i + 1 t_i > t_{i+1} ti>ti+1,因此 N i − M i > 0 N_i - M_i > 0 Ni−Mi>0,即前者所花时间比后者所花时间要多,于是证明要想得到等待时间之和最小,则必须满足任意两个数字必然要递增有序。
这种思想可以额外引申到操作系统的进程调度算法:最短作业优先处理。这样子我们就可以很清楚的知道假设我们只想要得到最短等待时间之和这一项指标的话,那么我们就可以参照这里的思想去理解。同时,平时考试的时候为什么大部分人需要采取的策略是先易后难,跟这里的思想也许也有一定的关系,更少的空白等待思考时间。还有这里给了我一个启发,生活中如果遇到多件不能并发处理并且都不是很紧急但是又都很有必要做的事情,我以后就要根据时间长短优先处理能够先处理的,这样可以减少空白等待的时间。
import java.util.*;
import java.io.*;
public class Main {
public static void main(String[] args) throws IOException{
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(System.out));
int n = Integer.parseInt(String.valueOf(reader.readLine()));
int[] arr = new int[n];
String[] num = reader.readLine().split(" ");
for (int i = 0; i < n; i++) {
arr[i] = Integer.parseInt(num[i]);
}
// 先排序
Arrays.sort(arr);
long res = 0;
for (int i = 0; i < n; i++) {
// 第i名打水的后面会有n-i-1个等待的,因此等待的时间就是arr[i] * (n-i-1)
res += (arr[i] * (n-i-1));
}
writer.write(res + "\n");
writer.flush();
writer.close();
reader.close();
}
}