大家好,我是晴天学长,二分的check函数,需要的小伙伴可以关注支持一下哦!后续会继续更新的。💪💪💪
1) .深秋的苹果
问题描述
当深秋的苹果树丰收时,村庄的居民们兴致勃勃地采摘着红彤彤的苹果。他们将采摘下来的N个苹果排成了一排,形成了-个苹果序列A,第i个苹果的甜度值为A; (1≤i≤N)。
现在村民需要将苹果序列划分为连续的M段,对于分割后的某一段Ar,定义其美味值表示为该段内不同下标的苹果的甜度两两相乘的总和.
注:如果某-段只有一个苹果,它的美味值为0。
请问应当如何给苹果分段,才能使得美味值最大的一段尽可能小,你只需要输出这个最大美味值可能的最小值即 可。
输入格式
第1行输入2个正整数N, M,分别为苹果序列的长度和需要分成的段数。
第2行输入N个空格隔开的正整数,表示苹果序列。
输出格式
输出仅1行,包含1个整数,表示管案。
样例输入
147258
样例输出
39
说明
我们可以把苹果序列分成[1,4,7,[2,5],[8],这样最大的一段美味值为39,是最小的分法。
评测数据规模
1≤M≤N≤20000。
1≤A;< 10000.
2) .算法思路
深秋的苹果(二分答案+前缀和)
1.接受数据,写一个前缀和数组,注意数据量有10的4次方
2.二分答案(右满足)最大的最小
3.判断是否能划分成M段(往后)
check 函数
ans+=
if j-1=-1, 就a[i]*sum[i]就好。
注意:
(1)如果以最大化分组都大于m,那只有一组最大化的时候,分的组数一定不会满足要求。
(2)当最大化分组数m小于等于M的时候,那一定能凑成M组。
3).算法步骤
1.使用前缀和将数组的值求和,预处理sum数组。
2.用二分查找法求出满足切分段数大于等于M的最小美味值下限l和上限r。
3.检查函数check中使用滑动窗口:
(1)left和right为窗口指针
(2)m记录当前段数
(3)ans记录当前窗口内所有元素和
(4)对右指针右移,计算加进来的元素贡献,同时维护ans是否超过mid
(5)每当ans超过mid,将left右移一位,ans清零。
(6)m加1。
(7)最后返回m是否小于等于M
(8)每次二分将mid作为美味值后,根据check函数结果决定收缩查找范围。
4.重复直到l=r时找到满足条件的最小美味值下限。
4). 代码实例
package LanQiaoTest.二分;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
public class 深秋的苹果 {
static BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
static String s;
static String[] sArray;
static long[] sum;
static int[] a;
public static void main(String[] args) throws IOException {
s = in.readLine();
sArray = s.split(" ");
int N = Integer.parseInt(sArray[0]);
int M = Integer.parseInt(sArray[1]);
s = in.readLine();
sArray = s.split(" ");
sum = new long[N];
a = new int[N];
//接受数据并前缀和
for (int i = 0; i < sArray.length; i++) {
a[i] = Integer.parseInt(sArray[i]);
if (i == 0) {
sum[i] = a[i];
;
} else {
sum[i] = sum[i - 1] + a[i];
}
}
//二分答案
System.out.println(mid(0L, (long) 4e18, sum, a, M));
}
private static long mid(long l, long r, long[] sum, int[] a, int M) {
while (l < r) {
long mid = (r - l) / 2 + l;
if (check(sum, a, mid, M)) {
l = mid + 1;
} else {
r = mid;
}
}
return l;
}
//如果能以mid的值分成m组,那一定能以mid+1分成m组。
private static boolean check(long[] sum, int[] a, long mid, int M) {
//以美味值mid分成M段
int m = 0;
int left = 0;
long ans = 0;
//滑动窗口
for (int right = 1; right < a.length; right++) {
if (left == 0) {
ans += ((long) a[right] * (sum[right] - a[right]));
} else {
ans += ((long) a[right] * (sum[right] - sum[left - 1] - a[right]));
}
while (ans > mid) {
left = right;
m++;
ans = 0;
}
}
//算上最后的那个组,不管是有没有满足mid,还是一个数(一个数的值是0)
m++;
//如果以最大化分组都大于m,那只有一组最大化的时候,分的组数一定不会满足要求
//当最大化分组数m小于等于M的时候,那一定能凑成M组。
return m > M;
}
}
4).总结
- 二分的正确写法。
- check函数的书写。
试题链接: