【js】Leetcode每日一题-完成所有工作的最短时间
【js】Leetcode每日一题-完成所有工作的最短时间
【题目描述】
给你一个整数数组 jobs ,其中 jobs[i] 是完成第 i 项工作要花费的时间。
请你将这些工作分配给 k 位工人。所有工作都应该分配给工人,且每项工作只能分配给一位工人。工人的 工作时间 是完成分配给他们的所有工作花费时间的总和。请你设计一套最佳的工作分配方案,使工人的 最大工作时间 得以 最小化 。
返回分配方案中尽可能 最小 的 最大工作时间 。
示例1:
输入:jobs = [3,2,3], k = 3
输出:3
解释:给每位工人分配一项工作,最大工作时间是 3 。
示例2:
输入:jobs = [1,2,4,7,8], k = 2
输出:11
解释:按下述方式分配工作:
1 号工人:1、2、8(工作时间 = 1 + 2 + 8 = 11)
2 号工人:4、7(工作时间 = 4 + 7 = 11)
最大工作时间是 11 。
提示:
1 <= k <= jobs.length <= 12
1 <= jobs[i] <= 107
【分析】
朴素递归模拟【dfs】
最容易想到的,模拟分配任务给每个工人,递归每个任务,使用idx结束递归,使用循环模拟分配给每个工人。
时间复杂度:\(O(k^n)\)
var minimumTimeRequired = function(jobs, k) {
let max = 1e8;
const kjobs = new Array(k).fill(0);
const dfs = function(idx){
if(idx == jobs.length){
let m = Math.max.apply(null, kjobs);
if(m < max){
max = m;
}
return;
}
for(let i=0;i<k;i++){
kjobs[i] += jobs[idx];
dfs(idx+1);
kjobs[i] -= jobs[idx];
}
}
dfs(0);
return max;
};
dfs+剪枝
可以想象,每个工人性质是相同的,所以模拟分配时,只需分配给第一个人,
比如,任务一81分配给工人1,则第一轮递归结束回到任务一时,任务一再分配给工人2时,这时分配给工人2其实等价于分配给工人1,后续分配均可以当作第一轮分配时工人1与工人2交换了位置。
所以,我们分配任务1时,可以默认给第一个人,然后分配任务2时,则只需分配给第一个人和第二个人(第二个人和后面的人性质相同),以此类推。
同时,如果当前分配的工人的任务时比已经得到的最小任务时长,可以直接剪掉。
时间复杂度:\(O(k!)\)
这样已经可以通过大部分样例。
/**
* @param {number[]} jobs
* @param {number} k
* @return {number}
*/
var minimumTimeRequired = function(jobs, k) {
// jobs.sort(function(a,b){return a<b?b:a;});
let max = 1e8;
const kjobs = new Array(k).fill(0);
const dfs = function(idx){
if(idx == jobs.length){
let m = Math.max.apply(null, kjobs);
if(m < max){
max = m;
}
return;
}
for(let i=0;i<k;i++){
kjobs[i] += jobs[idx];
if(kjobs[i] < max){ //剪枝1:当前分配的工人以及超过已知最小任务时
dfs(idx+1);
}
kjobs[i] -= jobs[idx];
if(idx == i) return; //剪枝2
}
}
dfs(0);
return max;
};
后来一看,剪枝2处并没有完全实现我们剪枝的原理,比如,当任务1、任务2都分配给工人1时,那么分配任务3时,工人2和后面的工人性质相同,而上面代码会分配给2后还会再分配给3才剪掉后面的。
/**
* @param {number[]} jobs
* @param {number} k
* @return {number}
*/
var minimumTimeRequired = function(jobs, k) {
// jobs.sort(function(a,b){return a<b?b:a;});
let max = 1e8;
const kjobs = new Array(k).fill(0);
const dfs = function(idx){
if(idx == jobs.length){
let m = Math.max.apply(null, kjobs);
if(m < max){
max = m;
}
return;
}
for(let i=0;i<k;i++){
kjobs[i] += jobs[idx];
if(kjobs[i] < max){ //剪枝1:当前分配的工人以及超过已知最小任务时
dfs(idx+1);
}
kjobs[i] -= jobs[idx];
if(kjobs[i] == 0) return; //剪枝2:当分配结束,此工人任务时为0,表示与模拟分配给后面工人均相同
}
}
dfs(0);
return max;
};
【官方题解】二分+贪心+剪枝+回溯+模拟
思路同在D天内送包裹的能力
取得最小工人任务时上下限,二分check这个时间是否是我们想要的。
check的方法与前面的回溯剪枝差不多。
var minimumTimeRequired = function(jobs, k) {
jobs.sort((a, b) => b - a);
let l = jobs[0], r = jobs.reduce(function(sum, curr){ return sum + curr });
while (l < r) {
const mid = Math.floor((l + r) >> 1);
if (check(jobs, k, mid)) {
r = mid;
} else {
l = mid + 1;
}
}
return l;
};
const check = (jobs, k, limit) => {
const workloads = new Array(k).fill(0);
return backtrack(jobs, workloads, 0, limit);
}
const backtrack = (jobs, workloads, i, limit) => {
if (i >= jobs.length) {
return true;
}
let cur = jobs[i];
for (let j = 0; j < workloads.length; ++j) {
if (workloads[j] + cur <= limit) {
workloads[j] += cur;
if (backtrack(jobs, workloads, i + 1, limit)) {
return true;
}
workloads[j] -= cur;
}
// 如果当前工人未被分配工作,那么下一个工人也必然未被分配工作
// 或者当前工作恰能使该工人的工作量达到了上限
// 这两种情况下我们无需尝试继续分配工作
if (workloads[j] === 0 || workloads[j] + cur === limit) {
break;
}
}
return false;
}
【js】Leetcode每日一题-完成所有工作的最短时间的更多相关文章
- 【js】Leetcode每日一题-制作m束花所需的最少天数
[js]Leetcode每日一题-制作m束花所需的最少天数 [题目描述] 给你一个整数数组 bloomDay,以及两个整数 m 和 k . 现需要制作 m 束花.制作花束时,需要使用花园中 相邻的 k ...
- 【js】Leetcode每日一题-数组异或操作
[js]Leetcode每日一题-数组异或操作 [题目描述] 给你两个整数,n 和 start . 数组 nums 定义为:nums[i] = start + 2*i(下标从 0 开始)且 n == ...
- 【js】Leetcode每日一题-解码异或后数组
[js]Leetcode每日一题-解码异或后数组 [题目描述] 未知 整数数组 arr 由 n 个非负整数组成. 经编码后变为长度为 n - 1 的另一个整数数组 encoded ,其中 encode ...
- 【js】Leetcode每日一题-叶子相似的树
[js]Leetcode每日一题-叶子相似的树 [题目描述] 请考虑一棵二叉树上所有的叶子,这些叶子的值按从左到右的顺序排列形成一个 叶值序列 . 举个例子,如上图所示,给定一棵叶值序列为 (6, 7 ...
- 【js】Leetcode每日一题-子数组异或查询
[js]Leetcode每日一题-子数组异或查询 [题目描述] 有一个正整数数组 arr,现给你一个对应的查询数组 queries,其中 queries[i] = [Li, Ri]. 对于每个查询 i ...
- 【js】Leetcode每日一题-停在原地的方案数
[js]Leetcode每日一题-停在原地的方案数 [题目描述] 有一个长度为 arrLen 的数组,开始有一个指针在索引 0 处. 每一步操作中,你可以将指针向左或向右移动 1 步,或者停在原地(指 ...
- 【js】Leetcode每日一题-二叉树的堂兄弟节点
[js]Leetcode每日一题-二叉树的堂兄弟节点 [题目描述] 在二叉树中,根节点位于深度 0 处,每个深度为 k 的节点的子节点位于深度 k+1 处. 如果二叉树的两个节点深度相同,但 父节点不 ...
- 【python】Leetcode每日一题-删除有序数组中的重复项
[python]Leetcode每日一题-删除有序数组中的重复项 [题目描述] 给你一个有序数组 nums ,请你 原地 删除重复出现的元素,使每个元素 最多出现一次 ,返回删除后数组的新长度. 不要 ...
- 【JavaScript】Leetcode每日一题-在D天内送包裹的能力
[JavaScript]Leetcode每日一题-在D天内送包裹的能力 [题目描述] 传送带上的包裹必须在 D 天内从一个港口运送到另一个港口. 传送带上的第 i 个包裹的重量为 weights[i] ...
随机推荐
- python 集合详解
字符串 一个个字符组成的有序的序列,时字符的集合 使用单引,双引,三引 引住的字符序列 字符时不可变的对象 bytes定义 bytes不可变字节序列 使用b前缀定义 只允许基本ASCII使用字符形式 ...
- Oracle dg下掉一个从库
1.在数据库中查找要下线的从库 SQL> show parameter log_archive_dest NAME TYPE VALUE----------------------------- ...
- C/C++ 性能优化背后的方法论:TMAM
开发过程中我们多少都会关注服务的性能,然而性能优化是相对比较困难,往往需要多轮优化.测试,属于费时费力,有时候还未必有好的效果.但是如果有较好的性能优化方法指导.工具辅助分析可以帮助我们快速发现性能瓶 ...
- 如何优雅地学习计算机编程-C++1
如何优雅的学习计算机编程--C++ 0.导入 如何优雅地学习计算机编程.我们得首先了解编程是什么?打个比方--写信. 大家都知道写信所用的语言双方都懂,这样的信才做到了信息交流,人和计算机也是如此人和 ...
- java例题_11 求不重复数
1 /*11 [程序 11 求不重复数字] 2 题目:有 1.2.3.4 这四个数字,能组成多少个互不相同且无重复数字的三位数?都是多少? 3 程序分析:可填在百位.十位.个位的数字都是 1.2.3. ...
- BST(二叉搜索树)的基本操作
BST(二叉搜索树) 首先,我们定义树的数据结构如下: public class TreeNode { int val; TreeNode left; TreeNode right; public T ...
- java 基础知识储备
初始JAVA JAVA 帝国的诞生 1972年C诞生 贴近硬件,运行极快,效率极高. 操作系统,编译器,数据库,网络系统等 指针和内存管理 1982年C++诞生 面向对象 兼容C 图形领域.游戏等 纵 ...
- Java入门环境的搭建
入门环境搭建 Java帝国的诞生 C & C++ 1972年C诞生 贴近硬件,运行极快,效率极高 操作系统,编译器,数据库,网络系统等 指针和内存管理 1982年C++诞生 面向对象 兼容C ...
- 用Python爬取网易云音乐热评
用Python爬取网易云音乐热评 本文旨在记录Python爬虫实例:网易云热评下载 由于是从零开始,本文内容借鉴于各种网络资源,如有侵权请告知作者. 要看懂本文,需要具备一点点网络相关知识.不过没有关 ...
- 01-MySQL Linux安装
一.检查当前系统是否安装过mysql rpm -qa|grep mysql 或 ps -ef|grep mysql 二.安装mysql服务端 rpm -ivh MySQL-server-5.5.48- ...