• 二分法的使用LeetCode1011送包裹,875吃香蕉


    LeetCode1011.https://leetcode-cn.com/problems/capacity-to-ship-packages-within-d-days/

    暴力解法:

    复杂写法,一次一次尝试,超时!!!

    // // 超时
    // class Solution {
    //     int[] sumN;
    //     public int shipWithinDays(int[] weights, int D) {
    //         // 前N个货物的总重量
    //         sumN = new int[weights.length];
    //         int curSum=0;
    //         for (int i = 0; i < weights.length; i++) {
    //             sumN[i] =curSum+weights[i];
    //             // System.out.println(sumN[i]);
    //             curSum += weights[i];
    //         }
    //         // 边界条件 
    //         if(D==1){
    //             return sumN[sumN.length-1];
    //         }
    //         sol(weights,0,0,0,D);
    
    //         return bestValue;
    //     }
    //     int bestValue = Integer.MAX_VALUE;
    //     /**
    //         * 有两种放法,自己单独放一堆,或者与前面的放一在堆
    //         * @param weights
    //         * @param curIndex
    //         * @param lastSum 上一个堆的和
    //         * @param maxSum 前面所有堆的最大值
    //         * @param D 剩余堆数
    //         * @return
    //         */
    //     public void sol(int[] weights,int curIndex,int lastSum,int maxSum, int D){
    //         // D为1则剩余所有放一堆(注!!!有可能D为一堆的时候可以将当前与之前的合并!!而不是往后放!!)
    //         // 应该改为D==0 的时候return D等于1 不需要return!!!
    
    //         if(D==0){
    //             return;
    //         }
    //         //(注!!!在没有return的条件下,不要修改形参中的值) 该语句不能省,当堆分配完必须计算后续结果,但不能return由于后面的可能会分配到已有的堆中。
    //         if(D==1){
    //             // 将curIndex和以后的堆放一起的情况
    //             int temp = Math.max(maxSum,sumN[sumN.length-1]-sumN[curIndex-1]);
    //             bestValue = Math.min(bestValue,temp);
    //         }
    //         if(curIndex  == weights.length ){
    //             bestValue = Math.min(bestValue,maxSum);
    //             return ;
    //         }
    //         // 当剩下的数刚好只剩D那么每一个数放一堆(注!!!在没有return的条件下,不要修改形参中的值)
    //         if(weights.length-curIndex == D){
    //             // 比较之前的堆,与剩余堆的最大值
    //             for(int i = curIndex; i<weights.length; i++){
    //                 maxSum = Math.max(maxSum,weights[i]);
    //             }
    //             bestValue = Math.min(bestValue,maxSum);
    //             return;
    //         }
    
    //         // 自己放一堆
    //         sol(weights,curIndex+1,weights[curIndex],Math.max(weights[curIndex],maxSum),D-1);
    
    //         // 和上一组放一堆(排除上一组没有的情况:第一次放)
    //         if(lastSum>0) {
    //             sol(weights, curIndex + 1, weights[curIndex] + lastSum, Math.max(weights[curIndex] + lastSum, maxSum), D);
    //         }
    //     }
    // }

    看了解答:二分法

    正确解:

    class Solution {
        // 利用二分法,找到承载能力,判断该值是否可以运完所有的货
        // 承载能力一定是在第一个货物的重量和所有货物重量之间的(存在问题)
        // 注!!!必须是最大的一个货物的重量开始,否则中间会有装不下的!!!
    
        public int shipWithinDays(int[] weights, int D) {
            // 前N个货物的总重量
            int[] sumN = new int[weights.length];
            int curSum=0;
            // 最大货物重量
            int maxSize=0;
            for (int i = 0; i < weights.length; i++) {
                maxSize = Math.max(maxSize,weights[i]);
                sumN[i] =curSum+weights[i];
                curSum += weights[i];
            }
            
            return midSearch(maxSize, curSum, sumN, D);
        }
        public int midSearch(int start, int end, int[] sumN, int D){
            if(start == end){
                return start;
            }
            // 找到当前中间承载量
            int mid = (start + end)/2;
            // System.out.println("尝试装载:"+mid);
            // 判断当前中间承载量能否满足
            if(canLoad(mid, D, sumN)){
                // 往左边找,因为右边一定可以承载
                return midSearch(start, mid, sumN, D);
            }else{
                // 不能承载,说明在右边
                return midSearch(mid+1, end, sumN, D);
            }
        }
        // 判断是否可以承载
        // 贪心
        public boolean canLoad(int load, int D, int[] sumN){
            // 上一次装的重量
            int lastload=0;
            // 装了几次,注!!!从1开始因为装不下当前值的时候,说明前面已经装了一次,本次开始是第二次!!!
            int times=1;
            // System.out.println("*****当前装载******"+load);
            for(int i=0; i<sumN.length; i++){
                // 下面成立表示当前货物装不下
                if(sumN[i] - lastload > load){        
                    lastload = sumN[i-1];
                    times++;
                    // System.out.println("当前装不下,上次装载量:"+lastload+",当前装载次数:"+times);
                }      
                if(times>D){
                    return false;
                }
            }
            // System.out.println("装载成功使用次数"+times);
            return true;
        }
    
    }

    看到类似题:

    Leetcode875

    class Solution {
        public int minEatingSpeed(int[] piles, int h) {
            // 类似Q1011送包裹问题二分法解决
            int maxPile=0;
            for(int i=0; i<piles.length; i++){
                maxPile = Math.max(maxPile,piles[i]);
            }
    
            return find(1, maxPile, piles, h);
    
        }
        // 在最小速度和最大速度之间
        public int find(int start, int end, int[] piles,int h){
            if(start == end){
                return start;
            }
            // 当前速度(取中值)
            int mid = (start+end)/2;
            System.out.println("当前速度:"+mid);
            if(canFinish(mid ,h ,piles)){
                return find(start, mid, piles, h);
            }else{
                return find(mid+1, end, piles, h);
            }
        }
        // 是否可以以当前速度完成
        public boolean canFinish(int V, int h, int[] piles){
            int times=0;
            for(int i=0; i<piles.length; i++){
                // times += (piles[i]/V + 1); 注!!!,当piles刚好为V时吃了两次,因此错误。所以使用向右取整数
                times += Math.ceil((float)piles[i]/V);
            }
            // 使用次数大于h表示吃不完
            if(times > h){
                System.out.println("只能吃"+h+"次,吃了"+times+"次,没吃完");
                return false;
            }
            System.out.println("只能吃"+h+"次,吃了"+times+"次,吃完了");
            return true;
        }
    }

    二分法思路:

    1.取值有一定的范围,从可能的最小的范围和最大的范围每次取中间值逼近。

    就如第一题送货,最小量是最大货物的重量,最大量是所有的货物重量之和。

    第二题中,最小量是1(每次吃1根香蕉),最大量是所有堆的最大值(一次只能吃一堆)。

    2.取中间值mid=(start+end)/2.

    3.判断中间值的情况:

    中间值如果可以满足,那么最后的结果一定在左边或者中间位置,那么递归调用solution(start,mid)。

    相反就是solution(mid+1,end)。

  • 相关阅读:
    CoreData学习-最好的一片文章
    Xcode4.6下添加百度地图ios版(BMapKit)详细教程(_BMKMapManager错误解决)
    NSSearchPathForDirectoriesInDomains用法
    编绎显示Unknown type name “CGFloat” 错误解决方法
    自动调整cell的高度
    sqlMapConfig约束
    PotPlayer左上角信息关闭
    松懈
    sql查询练习
    idea内容补充
  • 原文地址:https://www.cnblogs.com/wsZzz1997/p/14709494.html
Copyright © 2020-2023  润新知