【LeetCode每日一题】1723. 完成所有工作的最短时间
[1] 1723. 完成所有工作的最短时间
1.题目:
给你一个整数数组 jobs ,其中 jobs[i] 是完成第 i 项工作要花费的时间。
请你将这些工作分配给 k 位工人。所有工作都应该分配给工人,且每项工作只能分配给一位工人。工人的 工作时间 是完成分配给他们的所有工作花费时间的总和。请你设计一套最佳的工作分配方案,使工人的 最大工作时间 得以 最小化 。
返回分配方案中尽可能 最小 的 最大工作时间 。
2.示例①:
输入:jobs = [3,2,3], k = 3
输出:3
解释:给每位工人分配一项工作,最大工作时间是 3 。
3.示例②:
输入:jobs = [1,2,4,7,8], k = 2
输出:11
解释:按下述方式分配工作:
1 号工人:1、2、8(工作时间 = 1 + 2 + 8 = 11)
2 号工人:4、7(工作时间 = 4 + 7 = 11)
最大工作时间是 11 。
4.题解:
直接写dfs,容易超时。
class Solution {
public:
int ans;
int minimumTimeRequired(vector<int>& jobs, int k) {
ans = 0x3f3f3f3f;
vector<int> worker(k);
dfs(jobs, 0, worker, 0);
return ans;
}
void dfs(vector<int>& jobs, int i, vector<int> worker, int cur) {
if (i == jobs.size()) {
ans = min(ans, cur);
return;
}
for (int j = 0; j < worker.size(); j++) {
if (worker[j] + jobs[i] >= ans) continue;
worker[j] += jobs[i];
dfs(jobs, i + 1, worker, max(worker[j], cur));
worker[j] -= jobs[i];
}
}
};
第一种剪枝:当前累计大于等于最终结果时,直接不予考虑。
第二种剪枝:k个工人在同一层如果值相同,那么直接可以跳过,
第三种剪枝:这道题要求最小,那么加上从小到大排序,便可以达到剪枝效果。
class Solution {
public:
int ans;
int minimumTimeRequired(vector<int>& jobs, int k) {
ans = 0x3f3f3f3f;
vector<int> worker(k);
sort(jobs.begin(), jobs.end(), std::less<int>()); // 第三种
dfs(jobs, 0, worker, 0);
return ans;
}
void dfs(vector<int>& jobs, int i, vector<int> worker, int cur) {
if (i == jobs.size()) {
ans = min(ans, cur);
return;
}
set<int> s;
for (int j = 0; j < worker.size(); j++) {
if (s.count(worker[j])) continue; // 第二种
s.insert(worker[j]);
if (worker[j] + jobs[i] >= ans) continue; // 第一种
worker[j] += jobs[i];
dfs(jobs, i + 1, worker, max(worker[j], cur));
worker[j] -= jobs[i];
}
}
};
在给k个工人分配工作时,容易造成某个人分配工作过多,其他人闲置情况,为了解决这种问题,我们可以采用轮询办法。
假设有三个人,if执行完成。第一轮:退出dfs(used+1)语句, sum[used] = 0语句回退空余的丙。进入for(i < used),对下一个任务在甲乙之间分配(注意此时used为2,区间为甲乙,和dfs(used+1)的参数不同)。然后进入dfs,这时再进if,给空余的丙分配任务。
第二轮,退出dfs,回退空余的乙丙,进入for(i < used),对下一个任务在甲之间分配。然后进入dfs,这时再进if,给空余的乙丙分配任务。再次进入回溯步骤。
class Solution {
public:
int ans;
int minimumTimeRequired(vector<int>& jobs, int k) {
ans = 0x3f3f3f3f;
vector<int> worker(k);
// sort(jobs.begin(), jobs.end(), std::less<int>());
dfs(jobs, 0, 0, worker, 0);
return ans;
}
void dfs(vector<int>& jobs, int i, int used, vector<int> worker, int cur) {
if (i == jobs.size()) {
ans = min(ans, cur);
return;
}
if (used < worker.size()) {
worker[used] = jobs[i];
dfs(jobs, i + 1, used + 1, worker, max(worker[used], cur));
worker[used] = 0;
}
// set<int> s;
for (int j = 0; j < used; j++) {
// if (s.count(worker[j])) continue;
// s.insert(worker[j]);
if (worker[j] + jobs[i] >= ans) continue;
worker[j] += jobs[i];
dfs(jobs, i + 1, used, worker, max(worker[j], cur));
worker[j] -= jobs[i];
}
}
};
[2] 473. 火柴拼正方形
1.题目:
还记得童话《卖火柴的小女孩》吗?现在,你知道小女孩有多少根火柴,请找出一种能使用所有火柴拼成一个正方形的方法。不能折断火柴,可以把火柴连接起来,并且每根火柴都要用到。
输入为小女孩拥有火柴的数目,每根火柴用其长度表示。输出即为是否能用所有的火柴拼成正方形。
2.示例①:
输入: [1,1,2,2,2]
输出: true
解释: 能拼成一个边长为2的正方形,每边两根火柴。
3.示例②:
输入: [3,3,3,3,4]
输出: false
解释: 不能用所有火柴拼成一个正方形。
4.题解:
class Solution {
public:
bool makesquare(vector<int>& matchsticks) {
int ans = 0;
for (auto x : matchsticks) {
ans += x;
}
if (ans % 4 || !ans) return false;
vector<int> vex(4);
// 从大到小排序
sort(matchsticks.begin(), matchsticks.end(), std::greater<int>());
return dfs(matchsticks, 0, vex, ans / 4);
}
bool dfs(vector<int>& matchsticks, int i, vector<int> vex, int edge) {
if (i == matchsticks.size()) {
if (vex[0] == vex[1] && vex[1] == vex[2] && vex[2] == vex[3]) return true;
return false;
}
for (int j = 0; j < 4; j++) {
if (vex[j] + matchsticks[i] > edge || (j > 0 && vex[j] == vex[j-1])) continue;
// vex[i] == vex[i - 1]即上一个分支的值和当前分支的一样,
// 上一个分支没有成功,说明这个分支也不会成功,直接跳过即可。
vex[j] += matchsticks[i];
bool res = dfs(matchsticks, i + 1, vex, edge);
if (res) return true;
vex[j] -= matchsticks[i];
}
return false;
}
};
同第一题,也可以加上解决闲置问题策略。
class Solution {
public:
bool makesquare(vector<int>& matchsticks) {
int ans = 0;
for (auto x : matchsticks) {
ans += x;
}
if (ans % 4 || !ans) return false;
vector<int> vex(4);
sort(matchsticks.begin(), matchsticks.end(), std::greater<int>());
return dfs(matchsticks, 0, 0, vex, ans / 4);
}
bool dfs(vector<int>& matchsticks, int i, int used, vector<int> vex, int edge) {
if (i == matchsticks.size()) {
if (vex[0] == vex[1] && vex[1] == vex[2] && vex[2] == vex[3]) return true;
return false;
}
if (used < 4) {
vex[used] = matchsticks[i];
bool res = dfs(matchsticks, i + 1, used + 1, vex, edge);
if (res) return true;
vex[used] = 0;
}
for (int j = 0; j < used; j++) {
if (vex[j] + matchsticks[i] > edge || (j > 0 && vex[j] == vex[j-1])) continue;
vex[j] += matchsticks[i];
bool res = dfs(matchsticks, i + 1, used, vex, edge);
if (res) return true;
vex[j] -= matchsticks[i];
}
return false;
}
};