【二分查找、滑动窗口】P10389 [蓝桥杯 2024 省 A] 成绩统计|普及+

本文涉及的基础知识点

C++二分查找
C++算法:滑动窗口及双指针总结

[蓝桥杯 2024 省 A] 成绩统计

题目描述

小蓝的班上有 n n n 个人,一次考试之后小蓝想统计同学们的成绩,第 i i i 名同学的成绩为 a i a_i ai。当小蓝统计完前 x x x 名同学的成绩后,他可以从 1 ∼ x 1 \sim x 1∼x 中选出任意 k k k 名同学的成绩,计算出这 k k k 个成绩的方差。小蓝至少要检查多少个人的成

绩,才有可能选出 k k k 名同学,他们的方差小于一个给定的值 T T T?

提示: k k k 个数 v 1 , v 2 , ⋯   , v k v_1, v_2, \cdots , v_k v1,v2,⋯,vk 的方差 σ 2 \sigma^2 σ2 定义为: σ 2 = ∑ i = 1 k ( v i − v ˉ ) 2 k \sigma^2=\dfrac {\sum_{i=1}^k(v_i-\bar v)^2} k σ2=k∑i=1k(vi−vˉ)2,其中 v ˉ \bar v vˉ 表示
v i v_i vi 的平均值, v ˉ = ∑ i = 1 k v i k \bar v = \dfrac {\sum_{i=1}^k v_i} k vˉ=k∑i=1kvi。

输入格式

输入的第一行包含三个正整数 $n, k, T $,相邻整数之间使用一个空格分隔。

第二行包含 n n n 个正整数 a 1 , a 2 , ⋯   , a n a_1, a2, \cdots, a_n a1,a2,⋯,an ,相邻整数之间使用一个空格分隔。

输出格式

输出一行包含一个整数表示答案。如果不能满足条件,输出 − 1 -1 −1 。

样例 #1

样例输入 #1

5 3 1
3 2 5 2 3

样例输出 #1

4

提示

检查完前三名同学的成绩后,只能选出 $3, 2, 5 $,方差为 $1.56 $;

检查完前四名同学的成绩后,可以选出 $3, 2, 2 $,方差为 $0.22 < 1 $,所以答案为 $4 $。

对于 10 % 10\% 10% 的评测用例,保证 1 ≤ n , k ≤ 1 0 2 1 ≤ n, k ≤ 10^2 1≤n,k≤102;

对于 30 % 30\% 30% 的评测用例,保证 1 ≤ n , k ≤ 1 0 3 1 ≤ n, k ≤ 10^3 1≤n,k≤103 ;

对于所有评测用例,保证 $1 ≤ n, k ≤ 10^5 $,$1 ≤ T ≤ 2

^{31} -1 $,$1 ≤ a_i ≤ n $。

二分查找+滑动窗口

令平均成绩是vv,成绩依次是v1到vk。则方差的平方是: ∑ i : 1 k ( v v − v i ) 2 = k v v 2 − 2 v v ∑ i : 1 k v i + ∑ i : 1 k v i 2 \sum_{i:1}^k(vv-vi)^2=kvv^2-2vv\sum_{i:1}^kvi+\sum_{i:1}^kvi^2 ∑i:1k(vv−vi)2=kvv2−2vv∑i:1kvi+∑i:1kvi2

对a1到an升序排序。某最优解包括ai,且ai最小,则a[i...i+k-1]一定是不劣解。证明过程比较复杂,

二分类型:寻找首端

参数范围:[k,n]

Check函数:依次计算a[i...i+k-1]的方差是否小于等于L。任意一个小于等于返回真,否则返回false。

需要判断是否无解,无解返回-1。

获取配时:需要全局变量

编译时:需要全局变量

执行时:清空配方
性质一 :最大值减少后,仍然大于等于平均值,则方差变小或不变。

令平均成绩是aa,bi = ai-aa,显然sum(b)=0

令bk减少kd,bk >= kd

最后一项减少前减去减少后:bk^2 - (bk-kd)^2 = 2bkkd-kdkd

其它k-1项某项减少前减去减少后:bi^2-(bi-d)^2 = 2bid-d^2

相加:2bk(k-1)d+2sum(b)d-kdkd-(k-1)dd 因为sum(b)为0,故:

2bk(k-1)d-kdkd-(k-1)dd

因为d>0,约去d

2bk(k-1)-kkd+(k-1)d

bk>=kd

2kd(k-1)-kkd +(k-1)d

约去d

2kk-2k-kk+k-1

= kk-k-1

当k >1是,式子大于等于0,故减少kd是不劣解。

当k==0时,方差恒定1,也是不劣解。
本题证明 :

令某解最小成绩ai,最大成绩aj,ak没有选择, a i ≤ a k ≤ a j ai \leq ak \leq aj ai≤ak≤aj

如果ak >= aa,则根据性质一,aj替换aj。

如果ak < aa,则 将a进行如下变换 a[i] = x-(a[i]-x) ,再按性质一互换。

代码

核心代码

cpp 复制代码
#include <iostream>
#include <sstream>
#include <vector>
#include<map>
#include<unordered_map>
#include<set>
#include<unordered_set>
#include<string>
#include<algorithm>
#include<functional>
#include<queue>
#include <stack>
#include<iomanip>
#include<numeric>
#include <math.h>
#include <climits>
#include<assert.h>
#include<cstring>

#include <bitset>
using namespace std;

template<class T = int>
vector<T> Read() {
	int n;
	scanf("%d", &n);
	vector<T> ret(n);
	for(int i=0;i < n ;i++) {
		cin >> ret[i];
	}
	return ret;
}

template<class T = int>
vector<T> Read(int n) {
	vector<T> ret(n);
	for (int i = 0; i < n; i++) {
		cin >> ret[i];
	}
	return ret;
}

string ReadChar(int n) {
	string str;
	char ch;
	while (n--) {
		do
		{
			scanf("%c", &ch);
		} while (('\n' == ch));
			str += ch;
	}
	return str;
}
template<class T1,class T2>
void ReadTo(pair<T1, T2>& pr) {
	cin >> pr.first >> pr.second;
}

template<class INDEX_TYPE>
class CBinarySearch
{
public:
	CBinarySearch(INDEX_TYPE iMinIndex, INDEX_TYPE iMaxIndex, INDEX_TYPE tol = 1) :m_iMin(iMinIndex), m_iMax(iMaxIndex), m_iTol(tol) {}
	template<class _Pr>
	INDEX_TYPE FindFrist(_Pr pr)
	{
		auto left = m_iMin - m_iTol;
		auto rightInclue = m_iMax;
		while (rightInclue - left > m_iTol)
		{
			const auto mid = left + (rightInclue - left) / 2;
			if (pr(mid))
			{
				rightInclue = mid;
			}
			else
			{
				left = mid;
			}
		}
		return rightInclue;
	}
	template<class _Pr>
	INDEX_TYPE FindEnd(_Pr pr)
	{
		INDEX_TYPE leftInclude = m_iMin;
		INDEX_TYPE right = m_iMax + m_iTol;
		while (right - leftInclude > m_iTol)
		{
			const auto mid = leftInclude + (right - leftInclude) / 2;
			if (pr(mid))
			{
				leftInclude = mid;
			}
			else
			{
				right = mid;
			}
		}
		return leftInclude;
	}
protected:
	const INDEX_TYPE m_iMin, m_iMax, m_iTol;
};

class Solution {
		public:
			int Ans(vector<int>&a ,int k,long long T) {
				T *= k;
				const int N = a.size();		
				auto Check = [&](int mid) {
					double sum1=0, sum2 = 0;
					vector<int> b(a.begin() , a.begin() + mid);
					sort(b.begin(), b.end());
					for (int i = 0; i < b.size(); i++) {
						sum1 += b[i];
						sum2 += (double)b[i] * b[i];
						if (i >= k) {
							sum1 -= b[i - k];
							sum2 -= (double)b[i - k] * b[i - k];
						}
						if (i < k - 1) { continue; }
						const double avg = sum1 / k;
						const double cur = (k * avg * avg - 2 * sum1 * avg + sum2);
						if (cur <= T) { return true; }
					}
					return false;
				};
				auto ans= CBinarySearch<int>(k, N).FindFrist(Check);
				return Check(ans) ? ans : -1;
			}
		};

int main() {
#ifdef _DEBUG
	freopen("a.in", "r", stdin);
#endif // DEBUG
	int N, K,T;
	cin >> N >> K >> T ;
	auto a = Read<int>(N);
#ifdef _DEBUG	
	/*printf("N=%d,K=%d,T=%d,", N, K,T);
	Out(a, "a=");*/
#endif	
	auto res = Solution().Ans(a,K,T);
	cout << res << std::endl;
	return 0;
}

单元测试

cpp 复制代码
int N, K,T;
		vector<int> a;
		TEST_METHOD(TestMethod11)
		{
			N = 5, K = 3, T = 1, a = { 3,2,5,2,3 };
			auto res = Solution().Ans(a,K,T);
			AssertEx(4, res);
		}
		TEST_METHOD(TestMethod12)
		{
			N = 5, K = 3, T = 0, a = { 3,2,5,2,3 };
			auto res = Solution().Ans(a, K, T);
			AssertEx(-1, res);
		}

扩展阅读

我想对大家说的话
工作中遇到的问题,可以按类别查阅鄙人的算法文章,请点击《算法与数据汇总》。
学习算法:按章节学习《喜缺全书算法册》,大量的题目和测试用例,打包下载。重视操作
有效学习:明确的目标 及时的反馈 拉伸区(难度合适) 专注
闻缺陷则喜(喜缺)是一个美好的愿望,早发现问题,早修改问题,给老板节约钱。
子墨子言之:事无终始,无务多业。也就是我们常说的专业的人做专业的事。
如果程序是一条龙,那算法就是他的是睛
失败+反思=成功 成功+反思=成功

视频课程

先学简单的课程,请移步CSDN学院,听白银讲师(也就是鄙人)的讲解。
https://edu.csdn.net/course/detail/38771

如何你想快速形成战斗了,为老板分忧,请学习C#入职培训、C++入职培训等课程
https://edu.csdn.net/lecturer/6176

测试环境

操作系统:win7 开发环境: VS2019 C++17

或者 操作系统:win10 开发环境: VS2022 C++17

如无特殊说明,本算法用**C++**实现。

相关推荐
邴越18 分钟前
回文子序列问题解题模板
算法·leetcode·职场和发展
osir.36 分钟前
2025天梯训练1
c++·多关键字最短路
Zach_yuan1 小时前
list的模拟实现
c++·list
Flower#1 小时前
【图论】判断图中有环的两种方法及实现
算法·深度优先·图论
胡桃不是夹子1 小时前
学会了蛇形矩阵
c++·算法·矩阵
结衣结衣.1 小时前
【Qt】自定义信号和槽函数
开发语言·c++·qt·c++11
m0_675988231 小时前
Leetcode2597:美丽子集的数目
算法·leetcode·回溯·python3
丶重明1 小时前
【Go每日一练】返回切片中的最大值和最小值
算法·golang
一只_程序媛2 小时前
【leetcode hot 100 234】回文链表
算法·leetcode·链表
qq_433554542 小时前
C++ 二叉搜索树代码
开发语言·c++·算法