本文涉及的基础知识点
[蓝桥杯 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++**实现。