付账问题
文章目录
前言
上一次我们讲述了相邻的均分问题,这一次的均分问题没有距离限制,但是要求将标准差控制在最小值,也就是将整个函数的离散化控制在最小值,后续还会继续更新贪心有关的内容,感兴趣的小伙伴可以点个关注啦!
往期文章如下
雷达设备问题(从另一个角度看待区间合并 + 贪心思路 + 未发现关键的错误样例)
糖果传递问题(超详细的数论公式推导+贪心结论+均分问题)
问题描述
几个人一起出去吃饭是常有的事。但在结帐的时候,常常会出现一些争执。
现在有 n 个人出去吃饭,他们总共消费了 S 元。其中第 i 个人带了 ai 元。
幸运的是,所有人带的钱的总数是足够付账的,但现在问题来了:每个人分别要出多少钱呢?
为了公平起见,我们希望在总付钱量恰好为 S 的前提下,最后每个人付的钱的标准差最小。
这里我们约定,每个人支付的钱数可以是任意非负实数,即可以不是 1 分钱的整数倍。
你需要输出最小的标准差是多少。
标准差的介绍:标准差是多个数与它们平均数差值的平方平均数,一般用于刻画这些数之间的“偏差有多大”。
形式化地说,设第 i 个人付的钱为 bi 元,那么标准差为 :
输入格式
第一行包含两个整数 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
问题分析
注意观察标准差的公式,我们要将有些数是常数,比如说∑bi
,实际上就是总和s
,同理,n
也是知晓的。为了将标准差控制在最小值,我们要从离散化的角度入手。
对于那些带的钱高于平均值的人,我们要让他们出的钱尽可能贴近于平均值
对于那些带的钱低于平均值的老六,我们需要让他们交出全部的钱去贴近平均值
这里我们的平均值不是一成不变的,因为老六的缺钱,只能让钱多的大哥来垫底,
同时还要保障钱多的大哥出的钱贴合当前的平均值,因此,每次遍历一个老六,我们就要太高相应的平均值去让后面的大哥垫上钱【题目保证能够钱】
代码
详细的解析都在代码注释当中啦
#include<iostream>
#include<cmath>
#include<algorithm>
using namespace std;
const int N = 5e5 + 7;
int a[N];
long double s;
int n;
int main(){
cin>>n>>s;
for(int i=1;i<=n;i++) cin>>a[i];
long double ave=s/n,cur_ave=ave,sum=0;
//输入的数据和输出的数据要用long double来存储
//不然数据过不了
sort(a+1,a+1+n);//为什么要排序呢?
//这里我们要将老六全部放在在前面,防止那些有钱的大哥出少了钱
for(int i=1;i<=n;i++){
if(a[i]<cur_ave){
s-=a[i];
sum+=(a[i]-ave)*(a[i]-ave);
cur_ave=s/(n-i);//这里由于老六的缘故,要更新平均值
}
else{
sum+=(cur_ave-ave)*(cur_ave-ave);
}
}
printf("%.4Lf",sqrt(sum/n));
}