付賬問題
題目
幾個人一起出去喫飯是常有的事。
但在結帳的時候,常常會出現一些爭執。
現在有 n 個人出去喫飯,他們總共消費了 S 元。
其中第 i 個人帶了 ai 元。
幸運的是,所有人帶的錢的總數是足夠付賬的,但現在問題來了:每個人分別要出多少錢呢?
爲了公平起見,我們希望在總付錢量恰好爲 S 的前提下,最後每個人付的錢的標準差最小。
這裏我們約定,每個人支付的錢數可以是任意非負實數,即可以不是 1 分錢的整數倍。
你需要輸出最小的標準差是多少。
標準差的介紹:標準差是多個數與它們平均數差值的平方平均數,一般用於刻畫這些數之間的“偏差有多大”。
形式化地說,設第 i 個人付的錢爲 bi 元,那麼標準差爲 :
輸入格式
第一行包含兩個整數 n、S;
第二行包含 n 個非負整數 a1, …, an。
輸出格式
輸出最小的標準差,四捨五入保留 4 位小數。
數據範圍
輸入樣例
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;
}