【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每日一题-完成所有工作的最短时间的更多相关文章

  1. 【js】Leetcode每日一题-制作m束花所需的最少天数

    [js]Leetcode每日一题-制作m束花所需的最少天数 [题目描述] 给你一个整数数组 bloomDay,以及两个整数 m 和 k . 现需要制作 m 束花.制作花束时,需要使用花园中 相邻的 k ...

  2. 【js】Leetcode每日一题-数组异或操作

    [js]Leetcode每日一题-数组异或操作 [题目描述] 给你两个整数,n 和 start . 数组 nums 定义为:nums[i] = start + 2*i(下标从 0 开始)且 n == ...

  3. 【js】Leetcode每日一题-解码异或后数组

    [js]Leetcode每日一题-解码异或后数组 [题目描述] 未知 整数数组 arr 由 n 个非负整数组成. 经编码后变为长度为 n - 1 的另一个整数数组 encoded ,其中 encode ...

  4. 【js】Leetcode每日一题-叶子相似的树

    [js]Leetcode每日一题-叶子相似的树 [题目描述] 请考虑一棵二叉树上所有的叶子,这些叶子的值按从左到右的顺序排列形成一个 叶值序列 . 举个例子,如上图所示,给定一棵叶值序列为 (6, 7 ...

  5. 【js】Leetcode每日一题-子数组异或查询

    [js]Leetcode每日一题-子数组异或查询 [题目描述] 有一个正整数数组 arr,现给你一个对应的查询数组 queries,其中 queries[i] = [Li, Ri]. 对于每个查询 i ...

  6. 【js】Leetcode每日一题-停在原地的方案数

    [js]Leetcode每日一题-停在原地的方案数 [题目描述] 有一个长度为 arrLen 的数组,开始有一个指针在索引 0 处. 每一步操作中,你可以将指针向左或向右移动 1 步,或者停在原地(指 ...

  7. 【js】Leetcode每日一题-二叉树的堂兄弟节点

    [js]Leetcode每日一题-二叉树的堂兄弟节点 [题目描述] 在二叉树中,根节点位于深度 0 处,每个深度为 k 的节点的子节点位于深度 k+1 处. 如果二叉树的两个节点深度相同,但 父节点不 ...

  8. 【python】Leetcode每日一题-删除有序数组中的重复项

    [python]Leetcode每日一题-删除有序数组中的重复项 [题目描述] 给你一个有序数组 nums ,请你 原地 删除重复出现的元素,使每个元素 最多出现一次 ,返回删除后数组的新长度. 不要 ...

  9. 【JavaScript】Leetcode每日一题-在D天内送包裹的能力

    [JavaScript]Leetcode每日一题-在D天内送包裹的能力 [题目描述] 传送带上的包裹必须在 D 天内从一个港口运送到另一个港口. 传送带上的第 i 个包裹的重量为 weights[i] ...

随机推荐

  1. scrapy框架爬取图片并将图片保存到本地

    如果基于scrapy进行图片数据的爬取 在爬虫文件中只需要解析提取出图片地址,然后将地址提交给管道 配置文件中:IMAGES_STORE = './imgsLib' 在管道文件中进行管道类的制定: f ...

  2. elementUI实现日期框选中项文本高亮

    { margin: 0; font: 12px "Helvetica Neue" } p.p2 { margin: 0; font: 12px "PingFang SC& ...

  3. go调用python命令行参数过量报错python.exe: The filename or extension is too long.的解决方法

    当我们在调用python时,如果传入的参数数据量过大时会报错 python.exe: The filename or extension is too long. 这时候我们的解决办法是放弃传参,将想 ...

  4. Springboot项目启动后自动创建多表关联的数据库与表的方案

    文/朱季谦 在一些项目开发当中,存在这样一种需求,即开发完成的项目,在第一次部署启动时,需能自行构建系统需要的数据库及其对应的数据库表. 若要解决这类需求,其实现在已有不少开源框架都能实现自动生成数据 ...

  5. html Ajax连接.NET Ashx

    Html代码: <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <m ...

  6. ch1_6_1求解两种排序方法问题

    考拉有n个字符串字符串,任意两个字符串长度都是不同的.  考拉最近学习到有两种字符串的排序方法:   1.根据字符串的字典序排序.例如: "car" < "carr ...

  7. LuckyStar hctf2018

    LuckyStar hctf2018 程序注册有TLS回调函数 char __stdcall TlsCallback_0(int a1, int a2, int a3) { char result; ...

  8. C语言函数调用完整过程

    C语言函数调用详细过程 函数调用是步骤如下: 按照调用约定传参 调用约定是调用方(Caller)和被调方(Callee)之间按相关标准 对函数的某些行为做出是商议,其中包括下面内容: 传参顺序:是从左 ...

  9. frp实现内网穿透

    frp实现内网穿透 目标 通过外网访问内网设备,本文中实现通过手机的移动流量,可以访问到树莓派设备 设备准备 需要被访问的设备(本文中使用Raspberry Pi`).公网IP设备(本文中使用阿里云 ...

  10. 第四单元总结&期末总结

    OO第四单元总结&期末总结 一.第四单元总结 第一次作业 在第四单元的作业中,我的架构是逐步演进的.设计第一次作业的架构时并没有考虑到后续作业会增加新的图,所以直接把类图的实现放在UmlInt ...