蓝桥杯算法双周赛心得——深秋的苹果(二分+贪心分组前缀和)

大家好,我是晴天学长,二分的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函数的书写。

试题链接:

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

晴天学长

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值