题目
代码
#include<bits/stdc++.h>
using namespace std;
// 定义长整型变量的简写
#define ll long long
// 常量N定义为10万加10
const int N = 1e5+10;
// 定义变量n,k,t为长整型
ll n, k ,t;
// 定义avg和tmp为浮点型
float avg;
float tmp;
// 定义长度为N的数组a和b,存储输入的数据和排序后的数据
ll a[N], b[N];
// 定义长度为N的数组sum和sum2,分别用于累计求和以及平方和
ll sum[N];
ll sum2[N];
// 函数check用来检查在前num个数中是否存在k个数的方差小于t
bool check(ll num)
{
// 复制数组a到数组b中
for (int i = 1; i <= num; i++) b[i] = a[i];
// 对数组b中的前num个元素进行排序
sort(b+1, b+1+num);
// 计算累加和以及累加平方和
for(int i = 1; i <= num; i++)
{
sum[i] = sum[i-1] + b[i];
sum2[i] = sum2[i-1] + b[i]*b[i];
}
// 从第k个元素开始遍历,计算均值与方差
for(int i = k; i <= num; i++)
{
// 计算均值
avg = 1.0*(sum[i] - sum[i-k]) / k;
// 计算方差
tmp = sum2[i] - sum2[i-k];
// 如果方差小于t,则返回true
if(tmp/k - avg*avg < t) return true;
}
// 如果没有找到符合条件的子序列,则返回false
return false;
}
// 主函数
int main()
{
// 输入n,k,t
cin >> n >> k >> t;
// 读取输入的n个整数,并同时复制到数组b中
for(int i = 1; i <= n; i++) cin >> a[i], b[i] = a[i];
// 初始化二分查找的左界l,右界r
int l = k, r = n, mid;
// 执行二分查找
while(l < r)
{
// 计算中间位置
mid = (l+r) >> 1;
// 如果中间位置的子序列方差小于t,则将右界更新为mid
if(check(mid)) r = mid;
// 否则将左界更新为mid+1
else l = mid+1;
}
// 如果最终的左右边界相同并且不符合条件,则输出-1
if(l == r && !check(r)) cout << -1;
// 否则输出最终的答案
else cout << r;
return 0;
}
注意
不能一次性排序,而要不断加长不断排序。因为前后者产生出的k长连续序列是不同的。
avg的计算必须有1.0,因为avg的产生涉及除法,而各操作数都是非浮点数;tmp的计算则不然,不涉及除法所以不用1.0,之后tmp参与除法,此时tmp已经是浮点数则不需要1.0。一言以蔽之就是浮点数的产生计算中,如果涉及除法且各个操作数都不是浮点数,则要用1.0。
这里采用了排序、二分和前缀和。