考虑两个问题
1.如何确定x的取值?
2.当x的值固定时,如何选择这k个人,才能使得方差值最小?
问题一:
题目中求最小的x也就是大于等于x的时候都可以满足题目约束,小于就不行,到这里大概可以想到本题为二分题目,二分确定x的取值,那么如何判断是否村子一个x满足条件呢,这里只需要对x = n进行一个检查即可判断(check函数)
问题二:
x是一个固定值时,要在1~x之间任意选择k个人,使得方差最小,如果是暴力枚举最坏为o(n^2),想要使方差小,就需要选择的k个人的数值尽量接近,排序可以进行实现,进行枚举方案数即可o(n)
方差公式化简
预处理出来v^2和v的前缀和,进行o(1)时间查询
具体实现(o(nlog(n)log(n)))
import java.util.*;
public class Main {
static final int N = 100010;
static int[] arr = new int[N], backup = new int[N];
static long[] preA = new long[N], preAA = new long[N];
static int n, k, T;
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
n = sc.nextInt(); k = sc.nextInt(); T = sc.nextInt();
for (int i = 1; i <= n; i ++) {
arr[i] = sc.nextInt();
}
int ans = -1;
int l = k, r = n;
while (l < r) {//二分查找
int mid = l + r >> 1;
if (check(mid)) r = mid;
else l = mid + 1;
}
if (l >= k && l < n || l == n && check(l))
ans = l;
System.out.println(ans);
sc.close();
}
static boolean check(int mid) {
double cur = Double.MAX_VALUE;
for (int i = 1; i <= mid; i++) backup[i] = arr[i];//建立临时数组,不影响后续判断
Arrays.sort(backup, 1, mid + 1);//仅对1~mid的人进行排序,不考虑后面人的影响
for (int i = 1; i <= mid; i++) {//预处理前缀和
preA[i] = preA[i - 1] + backup[i];
preAA[i] = preAA[i - 1] + (long) backup[i] * backup[i];
}
//枚举
for (int i = k; i <= mid; i++) {
double vv = preAA[i] - preAA[i - k];
double v = preA[i] - preA[i - k];
double avg = v / k;
cur = Math.min(cur, vv + k * avg * avg - 2 * avg * v);
if (cur / k < T) return true;
}
return false;
}
}