微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

使用二分法来解决的问题

使用二分法来解决的问题

作者:Grey

原文地址:

博客园:使用二分法来解决的问题

CSDN:使用二分法来解决的问题

了解二分

参考:二分法基本思路和实现

以下是一些关于二分的经典习题。

分割数组的最大值

给定一个非负整数数组 nums 和一个整数 m ,你需要将这个数组分成 m 个非空的连续子数组。设计一个算法使得这 m 个子数组各自和的最大值最小。

OJ见:LeetCode 410. Split Array Largest Sum

PS: 此题也可以用四边形不等式优化的动态规划来解,但是最优解是二分法

思路

我们先求整个数组的累加和,假设累加和为sum,我们可以得到一个结论,分割的m个非空连续子数组的和的范围一定在(0,sum]区间内。转换一下思路,如果某种划分下的子数组之和的最大值为max,则max首先肯定在(0,sum]区间内。思路转换为:

子数组的累加和最大值不能超过max的情况下,最少可分多少部分?

假设能分k个部分,

如果k <= m,说明这种划分是满足条件的,我们看max是否可以变的更小。

如果k > m,说明这种划分是不满足条件的,我们需要调大max的值。

这里可以通过二分的方式来定位max的值。即max先取(0,sum]的中点位置,得到的划分部分k如果k <= m,则max继续去左边取中点位置来得到新的划分k,

如果k > mmax继续从右边的中点位置来得到新的划分k。

完整代码

class Solution {
    public static int splitArray(int[] nums, int m) {
        int sum = 0;
        for (int num : nums) {
            sum += num;
        }
        int l = 0;
        int r = sum;
        int ans = 0;
        while (l <= r) {
            int mid = l + ((r - l) >> 1);
            int parts = getParts(nums, mid);
            if (parts > m) {
                // mid越大,parts才会越小
                l = mid + 1;
            } else {
                ans = mid;
                r = mid - 1;
            }
        }
        return ans;
    }

    // 达到aim要分几部分
    public static int getParts(int[] nums, int aim) {
        for (int num : nums) {
            if (num > aim) {
                return Integer.MAX_VALUE;
            }
        }
        int part = 1;
        int all = nums[0];
        for (int i = 1; i < nums.length; i++) {
            if (all + nums[i] > aim) {
                part++;
                all = nums[i];
            } else {
                all += nums[i];
            }
        }
        return part;
    }
}

其中:int getParts(int[] nums, int aim)方法表示,在不超过aim的情况下,最少需要几个划分部分。方法的主要逻辑是:

遍历数组,如果发现某个元素的值超过了aim,直接返回系统最大,说明无法得到划分。如果没有超过aim,则继续加入下一个元素,直到超过aim,就定位出一个部分。依次类推,就可以得到最少有几个划分。整个算法时间复杂度O(N)

判断一个数是否是Step Sum

何为step sum? 比如680,680 + 68 + 6 = 754,所以680的Step Sum是754,给定一个正数num,判断它是不是某个数的Step Sum。

使用二分法来解决这个问题,思路如下:

如果一个数x的Step Sum为m,另外一个数y的Step Sum为n,如果x大于y,则m一定大于n。

给定一个数num,我们可以求0到num的中点位置mid,看这个位置的Step Sum是不是给定的num,如果是,直接返回true,如果中点位置的Step Sum大于num,则考虑在左半边继续二分,如果中点位置的Step Sum小于num,则考虑在右半边继续二分。

public class Code_0111_IsstepSum {
    public static boolean isstepSum(int stepSum) {
        int i = 0;
        int j = stepSum;
        while (i <= j) {
            int mid = i + ((j - i) >> 1);
            int value = stepSumOf(mid);
            if (value == stepSum) {
                return true;
            } else if (value < stepSum) {
                i = mid + 1;
            } else {
                // value > stepSum
                j = mid - 1;
            }
        }
        return false;
    }

    public static int stepSumOf(int num) {
        int v = 0;
        while (num != 0) {
            v += num;
            num = num / 10;
        }
        return v;
    }
}

经过对数器验证

package snippet;

import java.util.HashMap;

public class Code_0111_IsstepSum {
    public static boolean isstepSum(int stepSum) {
        int i = 0;
        int j = stepSum;
        while (i <= j) {
            int mid = i + ((j - i) >> 1);
            int value = stepSumOf(mid);
            if (value == stepSum) {
                return true;
            } else if (value < stepSum) {
                i = mid + 1;
            } else {
                // value > stepSum
                j = mid - 1;
            }
        }
        return false;
    }

    public static int stepSumOf(int num) {
        int v = 0;
        while (num != 0) {
            v += num;
            num = num / 10;
        }
        return v;
    }

    public static int stepSum(int num) {
        int sum = 0;
        while (num != 0) {
            sum += num;
            num /= 10;
        }
        return sum;
    }

    // for test
    public static HashMap<Integer, Integer> generateStepSumNumberMap(int numMax) {
        HashMap<Integer, Integer> map = new HashMap<>();
        for (int i = 0; i <= numMax; i++) {
            map.put(stepSum(i), i);
        }
        return map;
    }

    // for test
    public static void main(String[] args) {
        int max = 10000000;
        int maxStepSum = stepSum(max);
        HashMap<Integer, Integer> ans = generateStepSumNumberMap(max);
        System.out.println("测试开始");
        for (int i = 0; i <= maxStepSum; i++) {
            if (isstepSum(i) ^ ans.containsKey(i)) {
                System.out.println("出错了!");
            }
        }
        System.out.println("测试结束");
    }
}

未打印任何出错信息。代码通过。

更多

算法和数据结构笔记

参考资料

算法和数据结构体系班-左程云

原文地址:https://www.jb51.cc/wenti/3280274.html

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐