【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] ...
随机推荐
- .Net Core 2.1 升级3.1 问题整理
随着技术的不断拓展更新,我们所使用的技术也在不断地升级优化,项目的框架也在不断地升级,本次讲解 .net core 2.1 升级到3.1所需要注意的事项: 当项目框架升级后,所有的Nuget引用也会 ...
- List的主要实现类
//ArrayList:List的主要实现类 /* * List中相对于Collection,新增加的方法 * void add(int index, Object ele):在指定的索引位置inde ...
- PTE 准备之 Repeat sentence
Repeat sentence After listening to a sentence ,repeat the sentence 3-9 seconds 15 seconds Strategies ...
- Nodejs学习笔记(5) 文件上传系统实例
目录 2018.8.4更新: MySQL可以存放几乎任何类型的数据(图片.文档.压缩包等),但这不是最好的解决方案,正常情况下都是在数据库中存放文件路径,图片.音乐.视频.压缩包.文档等文件存放在硬 ...
- Django之cookie 与session组件
一.会话跟踪技术 1.1 什么是会话跟踪 我们需要先了解一下什么是会话!可以把会话理解为客户端与服务器之间的一次会晤,在一次会晤中可能会包含多次请求和响应.例如你给10086打个电话,你就是客户端,而 ...
- P1085_不高兴的津津(JAVA语言)
package 顺序与分支; /* * 题目描述 津津上初中了.妈妈认为津津应该更加用功学习,所以津津除了上学之外, 还要参加妈妈为她报名的各科复习班.另外每周妈妈还会送她去学习朗诵.舞蹈和钢琴. 但 ...
- 10、MyBatis教程之一对多处理
11.一对多处理 一对多的理解: 一个老师拥有多个学生 如果对于老师这边,就是一个一对多的现象,即从一个老师下面拥有一群学生(集合)! 1.实体类编写 @Data public class Stude ...
- java例题_41 利用递归给猴子分桃
1 /*41 [程序 41 猴子分桃] 2 题目:海滩上有一堆桃子,五只猴子来分.第一只猴子把这堆桃子平均分为五份,多了一个,这只猴子把 3 多的一个扔入海中,拿走了一份.第二只猴子把剩下的桃子又平均 ...
- Android Studio 之 ImageView 学习笔记
•参考资料 [1]:菜鸟教程 [2]:bilibili视频教程 •src和blackground的区别 background通常指的都是背景,而src指的是内容 当使用 src 填入图片时,是按照图片 ...
- windows环境下抓密码总结
在线抓密码 1.mimikatz privilege::debug token::whoami token::elevate lsadump::sam mimikatz.exe "privi ...