原题:
有 n 个人排队到 1 个水龙头处打水,第 i 个人装满水桶所需的时间是 ti,请问如何安排他们的打水顺序才能使所有人的等待时间之和最小?
输入格式
第一行包含整数 n。
第二行包含 n 个整数,其中第 i 个整数表示第 i 个人装满水桶所花费的时间 ti。
输出格式
输出一个整数,表示最小的等待时间之和。
数据范围
1≤n≤105,
1≤ti≤104输入样例:
7
3 6 1 4 2 5 7
输出样例:56
对题目中例子的分析:
最后的56是怎么得来的?
要想时间最少,那么就要求打水时间越少的人排在越前面打水。所以我们先排序,最终打水顺序为:
1,2,3,4,5,6,7 他们分别需要等待的时间为:
1:等0分钟
2:等1分钟
3:等1+2分钟
......
7:等1+2+3+4+5+6分钟
所以等待时间总和为:
sum=0+1+(1+2)+(1+2+3)+...+(1+2+3+4+5+6)
法一(贪心+前缀和):
代码:
#include<iostream>
#include<algorithm>
using namespace std;
const int N=1e5+10;
int a[N];
int s[N];
int main()
{
int n;
cin>>n;
for(int i=1;i<=n;i++) cin>>a[i];
sort(a+1,a+n+1); // 因为下标是从1开始的,所以排序sort()函数也要相应地改一下
for(int i=1;i<n;i++) // 循环条件不能写成 i<=n,因为这样会把最后一个人接水的时间也算上去,而实际上他接水的时间并不属于总的等待时间
{
s[i]=s[i-1]+a[i];
}
long long int sum=0;
for(int i=1;i<=n;i++) sum+=s[i];
cout<<sum;
return 0;
}
思路:
①输入,用数组存,注意下标从1开始,因为后面要用到前缀和,下标从1开始会更方便。
②用sort()函数对数组从小到大排序。
③求前缀和,s[i]=s[i-1]+a[i] 。(i从1开始)
④求时间和 sum+=s[i] 。
⑤输出 sum
法二(排序不等式):
代码:
#include<iostream>
#include<algorithm>
using namespace std;
const int N = 100005;
int main()
{
int a[N];
int n;
cin >> n;
for (int i = 0; i < n; i++)
{
cin >> a[i];
}
sort(a, a + n);
long long int sum = 0;
for (int i = 0; i < n; i++)// 也可以换成 i<n-1 因为最后一项是 sum+=a[n-1]*0,加相当于不加,所以最后一项可以省略
{
sum += a[i] * (n - 1 - i);
}
cout << sum;
}
思路:
从上面对题目例子的分析可以发现,实际上的总等待时间 sum 加了6次第一个人的打水时间,加了5次第二个的打水时间......,加了1次第六个人的打水时间,加了0次最后一个人的打水时间,所以可以推出公式:sum=a[1]*(n-1)+a[2]*(n-2)+a[3]*(n-3}+...+a[n-1]*1+a[n]*0