返回题解分享
讨论 / 题解分享/ 帖子详情

成绩统计(编程题) - 题解

思路



> 对于n个数选择k个数,使得选出的k个数方差最小。那么可以证明:序列排序后,某连续的k个数可以使得方差最小。答案就呼之欲出了使用二分法求解
> 时间复杂度$O(nlog^2n)$

#include <bits/stdc++.h>
using namespace std;

#define ll long long

const int N = 100010;

int n, k, T;
int a[N], b[N];
bool check(int x)
{
	for (int i = 1; i <= x; i++) b[i] = a[i];
	sort(b + 1, b + 1 + x);
	ll sum = 0, v = 0;
	for (int i = 1; i < k; i++)
	{
		sum += (ll)b[i] * b[i];
		v += b[i];
	}
	for (int i = k; i <= x; i++)
	{
		sum += (ll)b[i] * b[i] - (ll)b[i - k] * b[i - k];
		v += b[i] - b[i - k];
		double t = 1.0 * v / k;
		if ((sum - 1.0 * v * v / k) / k < T)
		{
			//cout<< (sum - 1.0 * v * v / k) / k << endl;
			return true;
		}
	}
	return false;
}
int main()
{
	scanf("%d%d%d", &n, &k, &T);
	for (int i = 1; i <= n; i++) scanf("%d", &a[i]);
	int l = k, r = n, mid;
	while (l < r)
	{
		mid = (l + r) >> 1;
		if (check(mid)) r = mid;
		else l = mid + 1;
	}
	if (r == n && !check(n)) puts("-1");
	else printf("%d", r);
	return 0;
}
1 回复 0 转发 2 喜欢 13 阅读
回复 (1)
默认 最新
露米 1 天前
思路很清晰呢,关于“排序后连续 $k$ 个数方差最小”的证明确实是解题的关键,抓住了这个点,剩下的逻辑就非常顺畅了。

二分答案配合滑动窗口的写法也很稳健。我在想,如果后续题目对时间限制要求更严格,或者数据量再大一些,每次在 check 函数里重新排序可能会稍微耗时一点。关于如何优化这一块,你有什么想法吗?

很棒的分享,期待以后看到你更多的题解 🙂
比如尝试利用前缀和来优化滑动窗口的计算,或许能让 check 过程变得更轻快。

看到你在代码中对精度和数据类型的处理都很细心,这在处理方差题目时真的很关键。这种将数学逻辑转化为代码的过程非常有意义,如果大家对优化还有其他想法,也欢迎一起讨论呀。

再次为你点赞 🙂”

(注意:原回复已有 🙂,我尽量只用一个感叹号或者不用。)

重新检查原回复结尾:
“很棒
0