[leetcode 双周赛 11] 1231 分享巧克力

摘要:
分割巧克力分享巧克力问题描述你有一大块巧克力,由不同甜味的小块组成。我们使用数组甜度来表示每一块的甜度。你计划和K个朋友分享这巧克力,所以你需要把它切K次才能得到K+1块。每一块由**块连续的**块组成。为了表示你的慷慨,你会吃最不甜的一块,其余的与你的朋友分享。请找出最佳的切割策略,以使巧克力的总甜味最大

1231 Divide Chocolate 分享巧克力

问题描述

你有一大块巧克力,它由一些甜度不完全相同的小块组成。我们用数组 sweetness 来表示每一小块的甜度。

你打算和 K 名朋友一起分享这块巧克力,所以你需要将切割 K 次才能得到 K+1 块,每一块都由一些 **连续 **的小块组成。

为了表现出你的慷慨,你将会吃掉 总甜度最小 的一块,并将其余几块分给你的朋友们。

请找出一个最佳的切割策略,使得你所分得的巧克力 总甜度最大,并返回这个 最大总甜度

示例 1:

输入: sweetness = [1,2,3,4,5,6,7,8,9], K = 5
输出: 6
解释: 你可以把巧克力分成 [1,2,3], [4,5], [6], [7], [8], [9]。

示例 2:

输入: sweetness = [5,6,7,8,9,1,2,3,4], K = 8
输出: 1
解释: 只有一种办法可以把巧克力分成 9 块。

示例 3:

输入: sweetness = [1,2,2,1,2,2,1,2,2], K = 2
输出: 5
解释: 你可以把巧克力分成 [1,2,2], [1,2,2], [1,2,2]。

提示:

  • 0 <= K < sweetness.length <= 10^4
  • 1 <= sweetness[i] <= 10^5

思路

  • 读题
  1. 将数组分成K+1份, 每份独立且包含是一连续子序列
  2. 计算每份元素之和, 使得最小那份是在所有可能分割方案中最大

贪心

假设必定有一最佳答案ans, 它的情况是:

  • 0 < ans <= sum(sweetness)/(K+1)
  • 每个分块之和必定 >= ans
  • 以ans为界限分割的块数量 >= (K+1)

先设ans=avg(sweetness, K+1), 期望最完美的情况下, 大家分到的一样多, 最少的肯定也是所有方案中最多的
如果以此做界限切割不到K+1块, 则缩减期望ans--
每次都假设当前期望ans是最佳切割方案的答案, 不断-1逼近正确答案

  • 贪心切割
    每次累加之和大于等于阈值, 就认为这是最佳划分, 作为1块
    最终判断是否能切割到(K+1)块

贪心+二分

最佳答案ans还有一个特性:

  • ans 是最佳答案
  • ans+1 不是答案
  • ans-1 是答案
    ans为界限, 大于它的都无法做出预期切割方案, 小于等于它的都可以做出切割方案, 其中等于它就是最佳切割方案

这样就有一个二分的特性
我们可以在一个确定有正确答案的范围内, 使用二分搜索, 不断调整范围区间, 直至找到正确答案
其中判断正确答案的位置时:

  • [left, right] mid = (left + right) >> 1
  • checkout(mid)
  1. 可以通过 left = mid+1 向右逼近最佳
  2. 不可以通过 right = mid-1 向左逼近最佳
  • checkout 就是之前的贪心切割

代码实现

贪心

/**
 * 超出时间限制 avg调整过慢
 */
class Solution {
    public int maximizeSweetness(int[] sweetness, int K) {
        // sum 该数组总和
        int sum = 0;
        for (int swt : sweetness) {
            sum += swt;
        }

        // avg 如果平均分给K+1个人
        int avg = sum / (K + 1);
        while (avg > 0) {
            // cur 当前分块个数
            // curSum 每个分块的大小
            int cur = 0, curSum = 0;
            for (int swt : sweetness) {
                curSum += swt;
                System.out.printf("cut:%d K:%d curSum:%d avg:%d
", cur, K, curSum, avg);
                if (curSum >= avg) {
                    System.out.println();
                    // 从第0块开始切 (cur++ > K or ++cur > (K+1))
                    if (cur++ >= K) {
                        return avg;
                    }
                    curSum = 0;
                }
            }
            // 以步长为1的"速度"向最佳答案靠近
            avg--;
        }

        return avg;
    }
}

贪心+二分

class Solution {
    public int maximizeSweetness(int[] sweetness, int K) {
        // ans 返回答案
        // left 二分左值
        // right 二分右值 大小为1e4*1e5
        int ans = 0, left = 0, right = (int) 1e9 + 50;

        // 最佳甜度必定在[left, right]区间内
        while (left <= right) {
            int mid = (left + right) >> 1;
            // 检测: 以mid为界限, 大于它的都不可以, 小于等于则可以
            if (check(sweetness, K + 1, mid)) {
                // 最佳mid值在后半段 [mid+1, right]
                left = mid + 1;
                ans = Math.max(mid, ans);
            } else {
                // 最佳mid值在前半段 [left, mid-1]
                right = mid - 1;
            }
        }

        return ans;
    }

    private boolean check(int[] arr, int len, int threshold) {
        // cur 在分块总和满足阈值threshold的情况下 可切块数量 --> k+1
        // sum 单分块之和
        int cur = 0, sum = 0;

        for (int a : arr) {
            sum += a;
            // 该连续块之和符合阈值 予以分块
            if (sum >= threshold) {
                cur++;
                sum = 0;
            }
        }

        // 如果在阈值threshold下 可以分成k+1块 该切割策略符合题意
        return cur >= len;
    }
}
  • 改进: 缩小二分查找范围
/**
 * 同上相同的思路
 * 不过使用二叉搜索的方式 并且进行了'剪枝'(最终结果必定不超过平均值, 缩小了二叉搜索范围)
 */
class Solution {
    public int maximizeSweetness(int[] sweetness, int K) {
        int sum = 0;
        for (int swt : sweetness) {
            sum += swt;
        }

        // right 采用平均值
        int left = 0, right = sum / (K + 1), ans = 0;

        while (left <= right) {
            int mid = (left + right) >> 1, cur = 0, curSum = 0;
            System.out.printf("[left:%d mid:%d right:%d]
", left, mid, right);
            for (int swt : sweetness) {
                curSum += swt;
                System.out.printf("[cut:%d K:%d] [curSum:%d mid:%d]
", cur, K, curSum, mid);
                if (curSum >= mid) {
                    if (cur++ >= K) {
                        break;
                    }
                    curSum = 0;
                }
            }

            if (cur > K) {
                ans = Math.max(ans, mid);
                left = mid + 1;
            } else {
                right = mid - 1;
            }
        }

        return ans;
    }
}
input:
[1,2,3,4,5,6,7,8,9]
5
output:
[left:0 mid:3 right:7]
[cut:0 K:5] [curSum:1 mid:3]
[cut:0 K:5] [curSum:3 mid:3]
[cut:1 K:5] [curSum:3 mid:3]
[cut:2 K:5] [curSum:4 mid:3]
[cut:3 K:5] [curSum:5 mid:3]
[cut:4 K:5] [curSum:6 mid:3]
[cut:5 K:5] [curSum:7 mid:3]
[left:4 mid:5 right:7]
[cut:0 K:5] [curSum:1 mid:5]
[cut:0 K:5] [curSum:3 mid:5]
[cut:0 K:5] [curSum:6 mid:5]
[cut:1 K:5] [curSum:4 mid:5]
[cut:1 K:5] [curSum:9 mid:5]
[cut:2 K:5] [curSum:6 mid:5]
[cut:3 K:5] [curSum:7 mid:5]
[cut:4 K:5] [curSum:8 mid:5]
[cut:5 K:5] [curSum:9 mid:5]
[left:6 mid:6 right:7]
[cut:0 K:5] [curSum:1 mid:6]
[cut:0 K:5] [curSum:3 mid:6]
[cut:0 K:5] [curSum:6 mid:6]
[cut:1 K:5] [curSum:4 mid:6]
[cut:1 K:5] [curSum:9 mid:6]
[cut:2 K:5] [curSum:6 mid:6]
[cut:3 K:5] [curSum:7 mid:6]
[cut:4 K:5] [curSum:8 mid:6]
[cut:5 K:5] [curSum:9 mid:6]
[left:7 mid:7 right:7]
[cut:0 K:5] [curSum:1 mid:7]
[cut:0 K:5] [curSum:3 mid:7]
[cut:0 K:5] [curSum:6 mid:7]
[cut:0 K:5] [curSum:10 mid:7]
[cut:1 K:5] [curSum:5 mid:7]
[cut:1 K:5] [curSum:11 mid:7]
[cut:2 K:5] [curSum:7 mid:7]
[cut:3 K:5] [curSum:8 mid:7]
[cut:4 K:5] [curSum:9 mid:7]

参考资源

第 11 场双周赛 全国排名
【算法实况】旅游期间打一场双周赛 - 力扣双周赛 - LeetCode Biweekly 11

免责声明:文章转载自《[leetcode 双周赛 11] 1231 分享巧克力》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇Redis简单入门安装(windows系统)Nginx配置静态文件问题下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

随便看看

Vlmcsd(KMS)激活服务器程序

vlmcs-Windows-x64.exe127.0.0.1检查服务器是否正常联通,端口1688。vlmcs-Windows-x64.exe-X127.0.0.1显示支持的激活app类型。...

PowerDesigner 15 使用技巧

1.检索PowerDesigner的调色板工具栏工具&gt;自定义大小工具栏&gt;调色板检查2。在表格工具中批量修改文本格式&gt;显示首选项&gt;选择符号中的所有项目&gt;...

fullcalendar日历控件知识点集合

除非对于极少的特殊需求,fullcalendar向我们提供的接口不足以满足,才会去改动fullcalendar本身的js文件。这些会议安排一般是保存在server的,在每次页面载入时,fullcalendar得到会议安排的集合,然后依照当中的日期去把事件描绘到日历相应的地方。...

OpenWrt路由器通过LuCI界面实现Guest SSID功能

此外,OpenWrt路由器上的访客SSID不会受到主SSID的MAC地址过滤功能的影响,这是番茄路由器的优势。...

Jboss

同时,为了扩大JBoss的企业市场,JBoss已经签署了许多渠道合作伙伴。2004年6月,JBoss宣布JBoss应用服务器已通过Sun公司的J2EE认证。这是JBoss应用服务器历史上最重要的里程碑。JBossAOP 1.0于2004年10月发布。这也证实了JBoss是一家创新型公司。JBoss应用服务器5.0于2008年12月6日正式发布。新版本的应用服...

ESXi挂载NFS共享存储

使用万兆交换机,ESXi使用NFS协议连接存储。本文介绍的是通过NFS协议挂载共享存储上的VS01卷,共享存储上已经赋予ESXi主机访问该卷的权限。...