[题目概述]
几个人一起出去吃饭是常有的事。
但在结帐的时候,常常会出现一些争执。
现在有 n 个人出去吃饭,他们总共消费了 S 元。
其中第 i 个人带了
a
i
a_i
ai 元。
幸运的是,所有人带的钱的总数是足够付账的,但现在问题来了:每个人分别要出多少钱呢?
为了公平起见,我们希望在总付钱量恰好为 S 的前提下,最后每个人付的钱的标准差最小。
这里我们约定,每个人支付的钱数可以是任意非负实数,即可以不是 1 分钱的整数倍。
你需要输出最小的标准差是多少。
标准差的介绍:标准差是多个数与它们平均数差值的平方平均数,一般用于刻画这些数之间的“偏差有多大”。
形式化地说,设第 i 个人付的钱为
b
i
b_i
bi
元,那么标准差为 :
输入格式
第一行包含两个整数 n、S;
第二行包含 n 个非负整数 a1, …, an。
输出格式
输出最小的标准差,四舍五入保留 4 位小数。
数据范围
1
≤
n
≤
5
×
1
0
5
1 ≤ n ≤ 5×10^5
1≤n≤5×105,
0
≤
a
i
≤
1
0
9
0 ≤ a_i ≤ 10^9
0≤ai≤109,
0
≤
S
≤
1
0
1
5
0 ≤ S ≤ 10^15
0≤S≤1015。
输入样例1:
5 2333
666 666 666 666 666
输出样例1:
0.0000
输入样例2:
10 30
2 1 4 7 4 8 3 6 4 7
输出样例2:
0.7928
现在感觉这贪心就是纯数学题 这题用到了均值不等式。
让我们求每个人付的钱数的标准差的最小值,也就是方差开根号,方差代表波动程度,那就是每个人付的钱最少最好,每个人都付账单的均值,这个想法的前提是你手里的前比均值大。要是手里的钱还不够付,那就有多少付多少,少付的在让其他人去均摊,这就是我们的第一步,猜想策略。(想不出来就嘎了)
接下来就是证明了
- a[i] >= s/n -> b[i] = s/n
- a[i] < s/n -> b[i] = a[i]
这样最难的这步证明也结束了,现在就根据这个贪心策略写代码就行。
- 完整代码
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
using namespace std;
const int N = 500010;
int a[N];
int n;
int main(){
long double s;
cin >> n >> s;
for(int i = 0; i < n; i ++) cin >> a[i];
// 必须要先排一次序,确保前面已经考虑的数有效
sort(a, a + n);
// long double是为了精度问题
long double sum = 0, avg = s / n;
for(int i = 0; i < n; i ++){
// cru是b[i],如果a[i] < cru,他就是账单的平均值
double cru = s / (n - i);
if(a[i] < cru) cru = a[i];
sum += (cru - avg) * (cru - avg);
s -= cru;
}
printf("%.4llf", sqrt(sum / n));
return 0;
}
- 本题的分享就结束了,还是比较难得,尤其是想策略的第一步,有问题的小伙伴可以留言。