贪心-第九届蓝桥杯省赛C++A组-付账问题

贪心-第九届蓝桥杯省赛C++A组-付账问题

题目:

几个人一起出去吃饭是常有的事。

但在结帐的时候,常常会出现一些争执。

现在有 n 个人出去吃饭,他们总共消费了 S 元。

其中第 i 个人带了 ai 元。

幸运的是,所有人带的钱的总数是足够付账的,但现在问题来了:每个人分别要出多少钱呢?

为了公平起见,我们希望在总付钱量恰好为 S 的前提下,最后每个人付的钱的标准差最小。

这里我们约定,每个人支付的钱数可以是任意非负实数,即可以不是 1 分钱的整数倍。

你需要输出最小的标准差是多少。

标准差的介绍:标准差是多个数与它们平均数差值的平方平均数,一般用于刻画这些数之间的“偏差有多大”。

形式化地说,设第 i 个人付的钱为 bi 元,那么标准差为 :

p1.png

输入格式
第一行包含两个整数 n、S;

第二行包含 n 个非负整数 a1, …, an。

输出格式
输出最小的标准差,四舍五入保留 4 位小数。

数据范围
1≤n≤5×105,
0≤ai,S≤109
输入样例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

题解:

目 标 式 : 设 人 均 消 费 为 b ˉ = S n , 目 标 s = ∑ i = 1 n ( b i − b ˉ ) 2 n 最 小 值 目标式:设人均消费为\bar{b}=\frac{S}{n},目标s=\sqrt{\frac{\sum_{i=1}^{n}(b_i-\bar{b})^2}{n}}最小值 bˉ=nSs=ni=1n(bibˉ)2

观 察 目 标 式 形 式 , 求 最 小 值 , 联 想 到 均 值 不 等 式 : 观察目标式形式,求最小值,联想到均值不等式: :

对 实 数 x 1 , x 2 , . . . , x n , 若 ∑ i = 1 n x i = C ( 常 数 ) , 则 有 ∑ i = 1 n x i 2 n > = ( ∑ i = 1 n x i ) 2 n = C 2 n , 当 且 仅 当 x 1 = x 2 = . . . = x n 时 取 等 。 对实数x_1,x_2,...,x_n,若\sum_{i=1}^{n}x_i=C(常数),则有\frac{\sum_{i=1}^{n}x_i^2}{n}>=\frac{(\sum_{i=1}^{n}x_i)^2}{n}=\frac{C^2}{n},当且仅当x_1=x_2=...=x_n时取等。 x1,x2,...,xn,i=1nxi=C()ni=1nxi2>=n(i=1nxi)2=nC2x1=x2=...=xn

那 么 对 于 目 标 式 , ∑ i = 1 n ( b i − b ˉ ) = ∑ i = 1 n b i − n b ˉ = S − S = 0 为 定 值 , 故 目 标 式 最 小 值 当 且 仅 当 b 1 = b 2 = . . . = b n = b ˉ 时 取 到 。 也 就 是 说 , ∣ b i − b ˉ ∣ 的 值 越 小 越 好 。 那么对于目标式,\sum_{i=1}^{n}(b_i-\bar{b})=\sum_{i=1}^{n}b_i-n\bar{b}=S-S=0为定值,\\故目标式最小值当且仅当b_1=b_2=...=b_n=\bar{b}时取到。也就是说,|b_i-\bar{b}|的值越小越好。 i=1n(bibˉ)=i=1nbinbˉ=SS=0b1=b2=...=bn=bˉ,bibˉ

所 以 对 a i > b ˉ 的 那 部 分 人 付 的 尽 量 要 少 。 所以对a_i>\bar{b}的那部分人付的尽量要少。 ai>bˉ

对 a i < b ˉ 的 人 , 能 付 多 少 付 多 少 , b i = a i 。 少 付 的 那 部 分 钱 b ˉ − a i 由 剩 下 所 有 的 钱 足 够 的 人 去 平 摊 , 即 所 有 a j − ( S − a i ) n − 1 > 0 的 人 去 分 摊 。 对a_i<\bar{b}的人,能付多少付多少,b_i=a_i。少付的那部分钱\bar{b}-a_i由剩下所有的钱足够的人去平摊,\\即所有a_j-\frac{(S-a_i)}{n-1}>0的人去分摊。 ai<bˉ,,bi=aibˉaiajn1(Sai)>0

注 意 : 每 当 有 一 个 人 付 的 钱 少 于 平 均 数 , 那 么 剩 下 的 人 需 要 分 摊 的 平 均 数 就 会 增 加 , 就 需 要 钱 更 多 的 那 部 分 人 去 分 摊 。 注意:每当有一个人付的钱少于平均数,那么剩下的人需要分摊的平均数就会增加,就需要钱更多的那部分人去分摊。

因 此 , 我 们 将 所 有 人 带 的 钱 a i 先 排 序 , 从 钱 少 的 人 先 考 虑 , 否 则 无 法 确 定 钱 多 的 人 需 要 分 摊 多 少 钱 。 因此,我们将所有人带的钱a_i先排序,从钱少的人先考虑,否则无法确定钱多的人需要分摊多少钱。 ai

代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#define ll long long
#define inf 0x7fffffff
using namespace std;
const int N=5e5+10;
int n;
double S,ans;
double a[N];
int main()
{
    cin>>n>>S;
    for(int i=0;i<n;i++)
        scanf("%lf",&a[i]);

    sort(a,a+n);
    double avg=S/n;
    for(int i=0;i<n;i++)
    {
        double cur=S/(n-i);//计算当前需要分摊的平均值
        if(a[i]<cur) cur=a[i];
        ans+=(cur-avg)*(cur-avg);
        S-=cur;//更新未付金额的总价值
    }

    ans=sqrt(ans/n);

    printf("%.4f\n",ans);
    return 0;
}

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值