问题描述
几个人一起出去吃饭是常有的事。
但在结帐的时候,常常会出现一些争执。
现在有 n 个人出去吃饭,他们总共消费了 S 元。
其中第 i 个人带了 ai 元。
幸运的是,所有人带的钱的总数是足够付账的,但现在问题来了:每个人分别要出多少钱呢?
为了公平起见,我们希望在总付钱量恰好为 S 的前提下,最后每个人付的钱的标准差最小。
这里我们约定,每个人支付的钱数可以是任意非负实数,即可以不是 1 分钱的整数倍。
你需要输出最小的标准差是多少。
标准差的介绍:标准差是多个数与它们平均数差值的平方平均数,一般用于刻画这些数之间的“偏差有多大”。
形式化地说,设第 i 个人付的钱为 bi 元,那么标准差为 :
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OHTOg5ng-1673831661293)(AcWing 蓝桥杯辅导.assets/19_6734517a16-p1.png)]
输入格式
第一行包含两个整数 n、S;
第二行包含 n 个非负整数 a1, …, an。
输出格式
输出最小的标准差,四舍五入保留 4 位小数。
数据范围
1≤n≤5×105,
0≤ai≤109,
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
思路
由题可知,标准差的公式如下:
根据均值不等式:
可知 x 1 + x 2 + . . . + x n x_1+x_2+...+x_nx
1
+x
2
+...+x
n
等价于 ( b 1 − s / n ) 2 + ( b 2 − s / n ) 2 + . . . + ( b n − s / n ) 2 (b_1-s/n)^2+(b_2-s/n)^2+...+(b_n-s/n)^2(b
1
−s/n)
2
+(b
2
−s/n)
2
+...+(b
n
−s/n)
2
,又因为 b 1 + b 2 + . . . + b n = s b_1+b_2+...+b_n=sb
1
+b
2
+...+b
n
=s 且 s / n ∗ n = s s/n*n=ss/n∗n=s,故 b 1 − s / n + b 2 − s / n + . . . + b n − s / n = s − s = 0 b_1-s/n+b_2-s/n+...+b_n-s/n=s-s=0b
1
−s/n+b
2
−s/n+...+b
n
−s/n=s−s=0。
所以我们可以得出如下结论:
当 a i > = s / n a_i>=s/na
i
>=s/n 时,取平均数,这里可由上面等式推出。
当 a i < s / n a_i<s/na
i
<s/n 时,b i = a i b_i=a_ib
i
=a
i
且 b i < a i b_i<a_ib
i
<a
i
,这里可由两个值的均值不等式推出(证明略)。
总的来说,就是如果带的钱够交总花费的平均数就至少交这个平均数;如果不够则有多少出多少,然后将平均数与其差值分摊到其他人身上即让别人帮你垫。
代码
#include<bits/stdc++.h> 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++) scanf("%d", &a[i]); sort(a, a + n); //对携带金额进行排序 //钱多的人扶持钱少的人 long double res = 0, avg = s / n; for (int i = 0; i < n; i++) { long double cur = s / (n - i); if (a[i] < cur) cur = a[i]; res += (cur - avg) * (cur - avg); s -= cur; } //打印结果 printf("%.4Lf\n", sqrt(res / n)); return 0; }