牛逼!同事在会议室扇了自己几个大嘴巴子~

科技   2024-12-07 14:01   陕西  

说实话,看到这个话题时,我心里就想:“这也太戏剧化了吧,连会议室外面都能听见!”不过仔细一想,这种情况倒也不算完全离谱,尤其是在职场压力大的情况下,谁没在那个不太聪明的领导面前憋过一肚子气呢?

说回来,这个同事真的是“顶得住”啊!不过,大家有没有想过,领导的反应是多么奇葩?你发个表格,大家一看打不开,居然不先检查一下是自己的问题,反而在群里催。

然后同事忍不住回了一句“有催的功夫不如自己点开看看”,本以为这算是个合理的提醒,结果立马引爆了领导的“疯魔模式”。

所以你看要治这种不讲道理的人,有时候要像这位“哥”一样,发个疯,一下子就规规矩矩了。

算法题:分割数组的最大值

又碰到一个经典的算法题:分割数组的最大值。一开始看到这个问题,我脑袋里就浮现出“二分查找+动态规划”的组合拳,不过细细想来,其实这道题目并不需要那么复杂的套路,稍微调整下思路,可能就能搞定。

好了,废话不多说,直接看题:

题目描述:

给定一个数组 nums 和一个整数 m,将这个数组分成 m 个子数组,使得每个子数组的和的最大值最小。换句话说,我们要尽可能平衡地划分这个数组,使得每个子数组的和尽可能小,但总和又得覆盖整个数组。

目标:

返回划分后的子数组的和的最大值的最小值。

思路分析:

这个题目其实是典型的二分查找+贪心算法的结合。我们可以通过二分查找来试探“可能的最大和”,然后用贪心算法来验证这个最大和能否满足条件。

关键点:
我们要做的就是通过二分查找来确定“分割时每个子数组的最大和”的范围,目标是最小化这个最大和。也就是说,我们需要找到一个最小的“最大和”,使得分割出来的子数组总数不超过 m

1. 二分查找的范围

  • 最小值:max(nums),即每个子数组的和至少要能容下数组中的最大元素。
  • 最大值:sum(nums),即整个数组作为一个子数组时的和。

2. 贪心策略

每次尽量让当前的子数组和不超过某个值(我们二分查找的当前猜测值),当超过时,就开始新的一组子数组。

3. 验证

对于每个二分查找的中间值,我们可以用贪心策略来验证这个值是否能分割成不超过 m 个子数组。如果可以,那么我们可以继续尝试更小的值;如果不行,那就得增加这个值。

代码实现:

def splitArray(nums, m):
    def canSplit(mid):
        # 尝试是否能将数组分割为 <= m 个子数组
        current_sum = 0
        count = 1  # 至少一个子数组
        for num in nums:
            if current_sum + num > mid:
                count += 1
                current_sum = num
                if count > m:  # 如果分成的子数组数量超过了m个,就返回 False
                    return False
            else:
                current_sum += num
        return True

    left, right = max(nums), sum(nums)
    while left < right:
        mid = (left + right) // 2
        if canSplit(mid):
            right = mid  # 如果可以分割,说明 mid 可能太大,需要减小
        else:
            left = mid + 1  # 如果不行,说明 mid 太小,需要增大
    return left

代码解析:

  1. **canSplit(mid)**:这是一个判断函数,接收一个值 mid,判断是否可以将数组分割成不超过 m 个子数组,并且每个子数组的和都不超过 mid

  • 我们从头开始遍历数组,累加当前子数组的和。如果当前和加上下一个元素超过了 mid,我们就开始一个新的子数组。
  • 如果在遍历过程中,子数组的数量超过了 m,说明 mid 太小,返回 False
  • 二分查找:通过二分查找来确定 mid 的值的范围。

    • 最小值是数组中最大的一个元素(不能比它小)。
    • 最大值是数组元素的总和(也不能比它大)。
    • 不断调整 leftright,直到找到合适的值。

    时间复杂度分析:

    • 时间复杂度:O(n * log(sum(nums) - max(nums)))。二分查找的次数是 O(log(sum(nums) - max(nums))),每次二分查找都要遍历一次数组 nums,所以时间复杂度是 O(n * log(sum(nums) - max(nums)))
    • 空间复杂度:O(1),除了输入数据外,我们只使用了常数空间。

    举个例子:

    假设 nums = [7, 2, 5, 10, 8]m = 2

    二分查找的初始范围是 left = 10(数组中的最大元素)和 right = 32(数组元素之和)。我们首先尝试 mid = 21,然后使用 canSplit 检查是否能将数组分割为 2 个子数组,使得每个子数组的和不超过 21。

    • 第一个子数组 [7, 2, 5] 和为 14,没超过 21。
    • 第二个子数组 [10, 8] 和为 18,也没超过 21。

    由于我们成功将数组分割为 2 个子数组,且每个子数组的和都不超过 21,所以我们继续缩小 mid,直到找到最小的符合条件的值。

    最后总结:

    这道题看似复杂,实则用二分查找和贪心策略就能解决,而且效率也非常高。通过二分查找我们能够快速定位可能的最大子数组和,而通过贪心算法来验证每个猜测值是否符合要求,最终找到最优解。

    对编程、职场感兴趣的同学,大家可以联系我微信:golang404,拉你进入“程序员交流群”。
    🔥虎哥私藏精品 热门推荐🔥

    虎哥作为一名老码农,整理了全网最全《python高级架构师资料合集》

    资料包含了《IDEA视频教程》《最全python面试题库》《最全项目实战源码及视频》《毕业设计系统源码》,总量高达650GB全部免费领取

    Python技术迷
    回复:python,领取Python面试题。分享AI编程,AI工具,Python技术栈,Python教程,Python编程视频,Pycharm项目,Python爬虫,Python数据分析,Python核心技术,Python量化交易。
     最新文章