[藍橋杯] 付賬問題

付賬問題

題目

幾個人一起出去喫飯是常有的事。

但在結帳的時候,常常會出現一些爭執。

現在有 n 個人出去喫飯,他們總共消費了 S 元。

其中第 i 個人帶了 ai 元。

幸運的是,所有人帶的錢的總數是足夠付賬的,但現在問題來了:每個人分別要出多少錢呢?

爲了公平起見,我們希望在總付錢量恰好爲 S 的前提下,最後每個人付的錢的標準差最小。

這裏我們約定,每個人支付的錢數可以是任意非負實數,即可以不是 1 分錢的整數倍。

你需要輸出最小的標準差是多少。

標準差的介紹:標準差是多個數與它們平均數差值的平方平均數,一般用於刻畫這些數之間的“偏差有多大”。

形式化地說,設第 i 個人付的錢爲 bi 元,那麼標準差爲 :
在這裏插入圖片描述

輸入格式

第一行包含兩個整數 n、S;

第二行包含 n 個非負整數 a1, …, an。

輸出格式

輸出最小的標準差,四捨五入保留 4 位小數。

數據範圍

1n5×1050ai,S1091≤n≤5×105 \\ 0≤ai,S≤109

輸入樣例

10 30
2 1 4 7 4 8 3 6 4 7

輸出樣例

0.7928

題解

思路

  • 首先算出平均值 s/n,把數據從小到大排序
  • 如果某個人的錢低於該值,那麼他將錢全部支付,然後其餘不夠的其他人平攤
#include <iostream>
#include <algorithm>
#include <cmath>

using namespace std;

const int N = 5 * 1e5 + 20;
int n;
double a[N], b[N], S;

int main () {
    cin >> n >> S;
    for (int i = 0; i < n; i ++) cin >> a[i];
    sort(a, a + n);
    
    double avg = (double)S / n;
    int start = 0;
    while (S > 0) {
        for (int i = start; i < n; i ++) {
            if (a[i] <= avg) {
                b[i] += a[i], S -= a[i], a[i] = 0;
                start = i + 1;
            }
            else {
                b[i] += avg, S -= avg, a[i] -= avg;
            }
        }
        avg = S / (n - start);
    }
    
    double sum = 0;
    for (int i = 0; i < n; i ++) sum += b[i];
    avg = sum / n, sum = 0;
    for (int i = 0; i < n; i ++) sum += (b[i] - avg) * (b[i] - avg);
    printf("%.4lf", sqrt(sum / n));
    
    return 0;
}

題解二

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <cmath>

using namespace std;

const int N = 500010;

int n;
int a[N];

int main()
{
    double s;
    scanf("%d%lf", &n, &s);
    for (int i = 0; i < n; i ++ ) scanf("%d", &a[i]);
    sort(a, a + n);

    double res = 0, avg = s / n;
    for (int i = 0; i < n; i ++ )
    {
        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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章