【二分查找、滑动窗口】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项某项减少前减去减少后:bi2-(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) ,再按性质一互换。
代码
核心代码
#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;
}
单元测试
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++**实现。