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

思路

读题

将数组分成K+1份, 每份独立且包含是一连续子序列

计算每份元素之和, 使得最小那份是在所有可能分割方案中最大

贪心

假设必定有一最佳答案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)

可以通过 left = mid+1 向右逼近最佳

不可以通过 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\n", 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]\n", left, mid, right);

for (int swt : sweetness) {

curSum += swt;

System.out.printf("[cut:%d K:%d] [curSum:%d mid:%d]\n", 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]

参考资源

java 分享巧克力_[leetcode 双周赛 11] 1231 分享巧克力相关推荐

  1. module-info.java是什么_刚接触jdk 11的模块系统,那么怎么处理module-info.java呢?

    我有一个老的SpringBoot项目,现在跑在jdk11上,能正常运行,但是在项目启动时会报错如下:java.lang.UnsupportedOperationException: Reflectiv ...

  2. [leetcode双周赛]5312. 大小为 K 且平均值大于等于阈值的子数组数目

    -_- 子数组,没好好看题目,我以为k个数...难怪感觉写的没问题,但是答案确不对. class Solution {public:int numOfSubarrays(vector<int&g ...

  3. [leetcode双周赛]5311. 将数字变成 0 的操作次数

    class Solution {public:int numberOfSteps (int num) {int res = 0;while(num > 0){if(num % 2 == 0){n ...

  4. LeetCode 第 59 场力扣夜喵双周赛(最短路径数+迪杰斯特拉、动态规划+最长公共前缀问题) / 第255场周赛(二进制转换,分组背包,子集还原数组(脑筋急转弯))

    第 59 场力扣夜喵双周赛 两道400多五百,后两道都写出代码来了,但是都有问题,哭辽- 还有刚开始第一道测试好慢,搞心态了 5834. 使用特殊打字机键入单词的最少时间 有一个特殊打字机,它由一个 ...

  5. [LeetCode周赛复盘] 第 89 场双周赛20221015

    [LeetCode周赛复盘] 第 89 场双周赛20221015 一.本周周赛总结 二. [Easy] 6208. 有效时间的数目 1. 题目描述 2. 思路分析 3. 代码实现 三.[Medium] ...

  6. LeetCode 第 30 场双周赛(477/2545,前18.7%,第2次全部通过)

    文章目录 1. 比赛结果 2. 题目 1. LeetCode 5177. 转变日期格式 easy 2. LeetCode 5445. 子数组和排序后的区间和 medium 3. LeetCode 54 ...

  7. LeetCode第 57 场力扣夜喵双周赛(差分数组、单调栈) and 第 251 场力扣周赛(状态压缩动规,树的序列化,树哈希,字典树)

    LeetCode第 57 场力扣夜喵双周赛 离knight勋章越来越近,不过水平没有丝毫涨进 1941. 检查是否所有字符出现次数相同 题目描述 给你一个字符串 s ,如果 s 是一个 好 字符串,请 ...

  8. Leetcode周赛复盘——第 71 场力扣双周赛与第 279 场力扣周赛

    双周赛: 5984. 拆分数位后四位数字的最小和 class Solution:def minimumSum(self, num: int) -> int:a, b, c, d = sorted ...

  9. LeetCode 第 31 场双周赛(273/2767,前9.87%,第3次全部通过)

    文章目录 1. 比赛结果 2. 题目 1. LeetCode 5456. 在区间范围内统计奇数数目 easy 2. LeetCode 5457. 和为奇数的子数组数目 medium 3. LeetCo ...

最新文章

  1. 16s及宏基因组测序公司资源--20161104
  2. 使用OKHttp3实现下载(断点续传、显示进度)
  3. 40款奇特的名片设计,吸引大家的眼球《上篇》
  4. 收集的常用css页面及表单表格样式
  5. 想要成为JAVA高手的25个学习目标
  6. PHP5: mysqli 插入, 查询, 更新和删除 Insert Update Delete Using mysqli (CRUD)
  7. 罗永浩最期待的游戏要来了 《Party Animals》10月上线
  8. linux环境变量设置和修改
  9. 拳魂觉醒服务器维护怎么办,拳魂觉醒 哪些不为人知的的小秘密
  10. 用AWK来过滤nginx日志中的特定值~~~
  11. CSS绝对底部布局 Sticky footer
  12. python循环语句打印矩形_pycharm软件python的一些循环语句的用法
  13. vue中keep-alive缓存功能使用详解
  14. 最长连续不重复子序列(双指针算法)
  15. 炭足迹计算机的火车好处,碳足迹与碳足迹计算器.pdf
  16. 【Django下载文件-Kml文件下载】
  17. 第一个java程序(输出个人信息)
  18. ZjDroid--脱壳神器介绍
  19. 汇编程序设计-11-AX、BX、CX、DX寄存器
  20. 百度推广优化(百度推广优化技巧)

热门文章

  1. FreeEIM 是班级的学习委员
  2. 我们就要想办法的s9t9
  3. 写 飞秋 程序,就是把简单的事情重复的做好
  4. (BCB) CComPtrIHTMLDocument2 FIEDoc;
  5. Java制作一个盒子程序_编写一个简单的Java程序,模拟计算器的功能。
  6. 吴思涵国内首场肿瘤ecDNA学术报告|深度揭秘半个世纪ecDNA的研究成果及突破性进展...
  7. 这一路,信念很简单,把书念下去,然后走出去,不枉活一世
  8. 2020 年诺贝尔生理奖授予丙肝病毒的3 位发现者
  9. 如何使Xcode占用更少的空间 Xcode占用空间太大解决方法
  10. adb shell命令_[Android]adb的使用