TreeviewCopyright © qgao 2021-* all right reserved, powered by aleen42

二分搜索

吃香蕉

力扣第 875 题

// 定义:速度为 x 时,需要 f(x) 小时吃完所有香蕉  
// f(x) 随着 x 的增加单调递减  
int f(int[] piles, int x) {  
    int hours = 0;  
    for (int i = 0; i < piles.length; i++) {  
    hours += piles[i] / x;  
    if (piles[i] % x > 0) {  
        hours++;  
    }  
    }  
    return hours;  
}  
public int minEatingSpeed(int[] piles, int H) {  
    int left = 1;  
    //题目说了1 <= piles[i] <= 10^9,也就是速度最快可以一小时内吃10^9  
    //开区间,需要加1  
    int right = 1000000000 + 1;  
    while (left < right) {  
    int mid = left + (right - left) / 2;  
    if (f(piles, mid) == H) {  
        // 搜索左侧边界,则需要收缩右侧边界  
        right = mid;  
    } else if (f(piles, mid) < H) {  
        // 需要让 f(x) 的返回值大一些  
        right = mid;  
    } else if (f(piles, mid) > H) {  
        // 需要让 f(x) 的返回值小一些  
        left = mid + 1;  
    }  
    }  
    return left;  
}

运送货物

力扣第 1011 题

// 定义:当运载能力为 x 时,需要 f(x) 天运完所有货物  
// f(x) 随着 x 的增加单调递减  
int f(int[] weights, int x) {  
    int days = 0;  
    for (int i = 0; i < weights.length; ) {  
    // 尽可能多装货物  
    int cap = x;  
    while (i < weights.length) {  
        if (cap < weights[i]) break; else cap -= weights[i];  
        i++;  
    }  
    days++;  
    }  
    return days;  
}  
public int shipWithinDays(int[] weights, int days) {  
    int left = 0;  
    // 注意,right 是开区间,所以额外加一  
    //最大载重显然就是weights数组所有元素之和,也就是一次把所有货物都装走。  
    int right = 1;  
    for (int w : weights) {  
    left = Math.max(left, w);  
    right += w;  
    }  
    while (left < right) {  
    int mid = left + (right - left) / 2;  
    if (f(weights, mid) == days) {  
        // 搜索左侧边界,则需要收缩右侧边界  
        right = mid;  
    } else if (f(weights, mid) < days) {  
        // 需要让 f(x) 的返回值大一些  
        right = mid;  
    } else if (f(weights, mid) > days) {  
        // 需要让 f(x) 的返回值小一些  
        left = mid + 1;  
    }  
    }  
    return left;  
}

分割数组的最大值

力扣第 410 题

①回溯暴力穷举可以在哪几个地方进行分割的组合个数,根据穷举结果去计算每种方案的最大子数组和。

/* 辅助函数,若限制最大子数组和为 max, 
计算 nums 至少可以被分割成几个子数组 */  
int split(int[] nums, int max) {  
    // 至少可以分割的子数组数量  
    int count = 1;  
    // 记录每个子数组的元素和  
    int sum = 0;  
    for (int i = 0; i < nums.length; i++) {  
    if (sum + nums[i] > max) {  
        // 如果当前子数组和大于 max 限制  
        // 则这个子数组不能再添加元素了  
        count++;  
        sum = nums[i];  
    } else {  
        // 当前子数组和还没达到 max 限制  
        // 还可以添加元素  
        sum += nums[i];  
    }  
    }  
    return count;  
}  
int splitArray(int[] nums, int m) {  
    // 一般搜索区间是左开右闭的,所以 hi 要额外加一  
    //最大子数组和max的取值范围显然是,子数组至少包含一个元素,或至多包含整个数组,  
    int lo = getMax(nums), hi = getSum(nums) + 1;  
    while (lo < hi) {  
    int mid = lo + (hi - lo) / 2;  
    // 根据分割子数组的个数收缩搜索区间  
    int n = split(nums, mid);  
    if (n == m) {  
        // 收缩右边界,达到搜索左边界的目的  
        hi = mid;  
    } else if (n < m) {  
        // 最大子数组和上限高了,减小一些  
        hi = mid;  
    } else if (n > m) {  
        // 最大子数组和上限低了,增加一些  
        lo = mid + 1;  
    }  
    }  
    return lo;  
}  
int getMax(int[] nums) {/* 计算数组中的最大值 */}  
int getSum(int[] nums) {/* 计算数组元素和 */}
Copyright © qgao 2021-* all right reserved,powered by Gitbook该文件修订时间: 2022-07-08 12:24:46

results matching ""

    No results matching ""