1235. 付账问题 - AcWing题库
几个人一起出去吃饭是常有的事。
但在结帐的时候,常常会出现一些争执。
现在有 nn 个人出去吃饭,他们总共消费了 SS 元。
其中第 ii 个人带了 aiai 元。
幸运的是,所有人带的钱的总数是足够付账的,但现在问题来了:每个人分别要出多少钱呢?
为了公平起见,我们希望在总付钱量恰好为 SS 的前提下,最后每个人付的钱的标准差最小。
这里我们约定,每个人支付的钱数可以是任意非负实数,即可以不是 11 分钱的整数倍。
你需要输出最小的标准差是多少。
标准差的介绍:标准差是多个数与它们平均数差值的平方平均数,一般用于刻画这些数之间的“偏差有多大”。
形式化地说,设第 ii 个人付的钱为 bibi 元,那么标准差为 :
输入格式
第一行包含两个整数 n、Sn、S;
第二行包含 nn 个非负整数 a1, …, ana1, …, an。
输出格式
输出最小的标准差,四舍五入保留 44 位小数。
数据范围
1≤n≤5×1051≤n≤5×105,
0≤ai≤1090≤ai≤109,
0≤S≤10150≤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],若a[i]>s/n,那么这个人就出s/n的钱;若a[i]<s/n,那么这个人就出a[i]的钱,让剩余几人均摊s/n-a[i]的钱。
有了大体思路,这里可以用均值不等式来证明贪心策略最优(证明略)。
由于每个a[i]的值都会影响以后的决策,所以将s/n动态计算即可。
for(int i=0; i<n; i++){
double cur = s/(n-i);//cur是当前应该均摊的钱数
if(f[i]<cur) cur = f[i];
s -= cur;//若钱够,则直接减去均摊的那一份;若不够,则让后面的人均摊差值
}
另外,由于给出的序列每个人的钱数有多有少,考虑一下极端情况,若钱数是降序,则前面钱多的不能均摊,后面钱少的无人均摊。所以按升序排列。
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
using namespace std;
const int N = 5e5+10;
int f[N];
int main(){
int n;
long double s;//由于数据范围是1e15,所以用到long double
cin>>n>>s;
for(int i=0; i<n; i++) scanf("%d",&f[i]);
long double avg = s/n, ans = 0;
sort(f,f+n);
for(int i=0; i<n; i++){
double cur = s/(n-i);
if(f[i]<cur) cur = f[i];
s -= cur;
ans += pow(cur-avg,2);//求平方和,最后输出时计算一下方差
}
printf("%.4Lf", sqrt(ans/n));//long double输出使用Lf
return 0;
}