蓝桥杯算法训练 礼物——Java(二分)详解

在这里插入图片描述
前提:必须要用BufferedReader才能AC!
*这道题为二分的模板题,不懂的可先做一下 木材加工 *
poj 2774 木材加工

Description
木材厂有一些原木,现在想把这些木头切割成一些长度相同的小段木头,需要得到的小段的数目是给定了。当然,我们希望得到的小段越长越好,你的任务是计算能够得到的小段木头的最大长度。
木头长度的单位是厘米。原木的长度都是正整数,我们要求切割得到的小段木头的长度也要求是正整数。
Input
第一行是两个正整数N和K(1 ≤ N ≤ 10000, 1 ≤ K ≤ 10000),N是原木的数目,K是需要得到的小段的数目。
接下来的N行,每行有一个1到10000之间的正整数,表示一根原木的长度。
Output
输出能够切割得到的小段的最大长度。如果连1厘米长的小段都切不出来,输出”0”。

礼物的AC代码:

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Scanner;
import java.util.zip.CheckedInputStream;

public class Main {
    static long[] sum = new long[1000010];
    static int[] arr = new int[1000010];
    public static void main(String[] args) throws IOException {
        BufferedReader sc = new BufferedReader(new InputStreamReader(System.in));
        // N,S
        String[] ss = null;
        String mm = sc.readLine();
        ss = mm.split(" ");
        int N = Integer.parseInt(ss[0]);
        long S = Long.parseLong(ss[1]);
        //一排石子
        String[] pp = null;
        String qq = sc.readLine();
        pp = qq.split(" ");
        //获取前缀和
        for(int i = 1;i<=N;i++){
            arr[i] = Integer.parseInt(pp[i-1]);
            sum[i] = sum[i-1] + arr[i];
        }
        //二分
        int l = 1,r = N;
        while(l < r){
            int mid = (l+r+1) >> 1;
            if(check(mid,N,S)){
                l = mid;
            }else{
                r = mid-1;
            }
        }
        //答案是2*l
        System.out.println(2*l);
    }
    public static boolean check(int mid,int N,long S){
    //为什么有for? 其实你找到的mid只是k(2*k个石子的一半),
    //也就是带走石子数量的一半,你要在一排石子中选出一段(长度为2*k)
    //并拿出前k和后k个石子看看满不满足<=S,若有某一段满足,
    //就代表该k可取,那就再经过二分扩大一下k,看看还满不满足,
    //经过几轮操作后,目标就锁定在最右侧,即得到最优答案
        for(int i = mid;i<=N-mid;i++){
            if(sum[i]-sum[i-mid] <= S && sum[i+mid]-sum[i] <= S){
                return true;
            }
        }
        return false;
    }
}

二分模板

check函数就是按题意的限制条件

//尽量往左找目标
while (l < r)
    {
        int mid = (l+r) / 2;
        if (check(mid))  r = mid;
        else l = mid + 1;
    }
//在往右找目标
while (l < r)
    {
        int mid = (l + r + 1) / 2;
        if (check(mid))  l = mid;
        else r = mid - 1;
    }
  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Uranus^

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

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

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

打赏作者

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

抵扣说明:

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

余额充值