【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] ...
随机推荐
- 如何获取下载 FreeBSD
『如何获取下载 FreeBSD 』 『如何获取下载 FreeBSD 』 FreeBSD 是免费获取的. [下载地址] O网页链接 版本选择,尽量选择较新版本,桌面用户可选择 current 版本.st ...
- 1x1卷据层的作用
1. 尽管1x1的卷据不会捕捉空间特征(spatial features/patterns,直观点说就是2D平面图的纹理特征),但是他们能很好基于通道(channel-wise/along the d ...
- 用jar命令打包war远程部署
最近在看jboss的相关漏洞,用jmx-console进行war远程部署的时候碰到一个jsp转war的问题,研究了半天,记录一下免得搞忘了. 一开始网上是说的直接把jsp文件压缩成zip,再把后缀名改 ...
- ls:未找到命令
解决,别问为什么. 执行 export PATH=/bin:/usr/bin:$PATH
- CI/CD版本回滚Jenkins解决方案
一.创建项目 填写项目名,关系到项目路径对应请谨慎命名 二.项目配置 1.配置字符串参数和选项参数 2.代码仓库配置 3.构建环境 4.构筑脚本配置 5.点击左下方的保存或者应用 三.使用方法 1.发 ...
- k8s多集群配置管理平台
k8s多集群配置管理平台 临时集群特性 模拟生产环境 整体环境说明 内网:10.17.1.44 [root@localhost account-server]# kubectl get nodes N ...
- ATMS中去pause Activity A.
上文写完之后,感觉这个部分写的还是不清晰,本文继续补充一下. 首先还是看堆栈. obtain:78, PauseActivityItem (android.app.servertransaction) ...
- 微信小程序授权登录以及用户信息相关接口调整导致授权框不弹出
前言:4月8号升级了小程序业务后提交了版本并上线.突然一个同事说体验版的点击"登录"按钮无效.当时觉得应该不会呀,这几天一直用手机调试,每天也在不停的登录授权,弹框一直有的呀.然后 ...
- 面试关于Spring循环依赖问题,我建议你这么答!
写在前面 在关于Spring的面试中,我们经常会被问到一个问题:Spring是如何解决循环依赖的问题的. 这个问题算是关于Spring的一个高频面试题,因为如果不刻意研读,相信即使读过源码,面试者也不 ...
- Spring Boot 接口幂等插件使用
幂等概述 幂等性原本是数学上的概念,即使公式:f(x)=f(f(x)) 能够成立的数学性质.用在编程领域,则意为对同一个系统,使用同样的条件,一次请求和重复的多次请求对系统资源的影响是一致的. 幂等性 ...