Java实现最佳牛围栏_差分


 其实这个题,经典一看,可能是动态规划,但是我们分析一下数据范围,十万,我们就要找找其他方向了

   问围起来的数量的平均值最大是多少,得到两个关键点,

       1.平均值的最大

       2.要多少个围起来(田地数量必须大于F),

   1.平均值我们可以用二分法来确定,二分的范围是0到整块田地的数组和

   2.至少F个田地围起来,我们可以看相对于平均值的浮动,我们先把每个值减去平均值,

       然后我们用前缀和来判断这段田地的平均值是大是小

       大于0就是比平均值大(证明真实的平均值高于当前平均值,还可以向上二分),小于0就是比平均值小(证明真实的平均值低于当前平均值,只能向下二分)


   3.用双指针来确保最少F个田地围起来,左指针保存最小的前缀和,右指针不断向右扩大,两个前缀之差,就是这段范围的变化量


package Acwing;

import java.util.Scanner;

/**
* ClassName: _102最佳牛围栏
* Package: Acwing
*
* @DATE: 2022/6/16 6:26
* Author: asleep
*/
public class _102最佳牛围栏 {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
int f = sc.nextInt();
int[] num = new int[n];
double left = 0, right = 0;
for (int i = 0; i < n; i++) {
num[i] = sc.nextInt();
right = Math.max(right, num[i]); //二分范围的最大值为某段田地的最大值(平均值 <= 最大值)
}
while (left + 1e-5 < right) { //开始二分
double mid = (left + right) / 2.0;
double[] sum = new double[n];
sum[0] = num[0] - mid; //开始计算前缀和
for (int i = 1; i < n; i++) {
sum[i] = sum[i - 1] + num[i] - mid;
}
double min = 0;
boolean flag = false;
if (sum[f - 1] >= min) { //前f个前缀和大于0,则证明符合条件,可以向上二分平均值
flag = true;
}
for (int i = 0, j = f ; j < n; i++, j++) { //双指针确定范围 >=f
min = Math.min(min, sum[i]); //找到前缀和最小的那个
if (sum[j] - min >= 0) { //当前范围平均值大于0,可以向上二分平均值
flag = true;
break;
}

}
if (flag) { //向上还是向下二分
left = mid;
} else {
right = mid;
}
}
System.out.println((int)(right * 1000)); //转int会自动向下取整的


}
}