【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. springboot注解之@Configuration 和 @Bean

    1.包结构 2.主程序类 1 /** 2 * 主程序类 3 * @SpringBootApplication:这是一个springboot应用 4 * 5 * @SpringBootApplicati ...

  2. Reactive Spring实战 -- 响应式Redis交互

    本文分享Spring中如何实现Redis响应式交互模式. 本文将模拟一个用户服务,并使用Redis作为数据存储服务器. 本文涉及两个java bean,用户与权益 public class User ...

  3. ResNet论文笔记

    其实ResNet这篇论文看了很多次了,也是近几年最火的算法模型之一,一直没整理出来(其实不是要到用可能也不会整理吧,懒字头上一把刀啊,主要是是为了将resnet作为encoder嵌入到unet架构中, ...

  4. A New Stone Game POJ - 1740

    题目链接:https://vjudge.net/problem/POJ-1740#author=0 题意:有n堆石子,每次你可以选一堆拿走任意数量的石子,而且你还可以选择从这一堆剩下石子中取任意数量石 ...

  5. MyEclipse安装过程

    1.安装JDK并配置环境变量 下载地址: https://www.oracle.com/technetwork/java/javase/downloads/index.html ①点击download ...

  6. (原创)在Linux上安装运行Python3(CentOS7为例)

    在win10上开发好的python项目要部署在Linux上要面对的问题:怎么在Linux上跑py文件呢? 以Lunix CentOS7.x平台为例,CentOS系统上自带的已有python2.x 的版 ...

  7. istio服务条目(ServiceEntry)介绍

    使用服务条目资源(ServiceEntry)可以将条目添加到 Istio 内部维护的服务注册表中.添加服务条目后,Envoy 代理可以将流量发送到该服务,就好像该服务条目是网格中的服务一样.通过配置服 ...

  8. ECharts绘制折线图

    首先看实现好的页面 实现 首先引入echarts工具 // vue文件中引入echarts工具 let echarts = require('echarts/lib/echarts') require ...

  9. [Fundamental of Power Electronics]-PART I-4.开关实现-4.1 开关应用

    4.1 开关应用 4.1.1 单象限开关 理想的SPST(Single pole single throw)开关如图4.1所示.开关包含电源端子1和0,其电流和电压极性如图所示.在接通状态下,电压\( ...

  10. 还在用KPI做管理研发团队?试试黄勇的OKR实战笔记

    OKR是一种融入了人性的科学管理框架,承诺的事情就要努力去做到.深层次来看,OKR便恰恰体现了这样一种"承诺"精神. OKR绝不是一款简单的目标管理工具,用好它,你便能体会到管理的 ...