一. 题目
项目组共有N个开发人员,项目经理接到了M个独立的需求,每个需求的工作量不同,且每个需求只能由一个开发人员独立完成,不能多人合作。假定各个需求直接无任何先后依赖关系,请设计算法帮助项目经理进行工作安排,使整个项目能用最少的时间交付。
输入描述:
第一行输入为M个需求的工作量,单位为天,用逗号隔开。
例如:X1 X2 X3 … Xm
表示共有M个需求,每个需求的工作量分别为X1天,X2天…Xm天。其中0<M<30;0<Xm<200
第二行输入为项目组人员数量N
例如:
5
表示共有5名员工,其中0<N<10
输出描述:
最快完成所有工作的天数
例如:
25
表示最短需要25天能完成所有工作
补充说明:
收起
示例1
输入:
6 2 7 7 9 3 2 1 3 11 4
2
输出:
28
说明:
共有两位员工,其中一位分配需求 6 2 7 7 3 2 1共需要28天完成,另一位分配需求 9 3 11 4 共需要27天完成,故完成所有工作至少需要28天。
二.解题思路
这个问题可以通过贪心算法来解决。贪心算法的基本思想是每一步都选择当前状态下的最优解,以期望最终能够得到全局最优解。
在这个问题中,我们可以按照工作量从大到小的顺序,依次将每个需求分配给当前工作量最小的开发人员。这样可以确保工作量较大的需求优先分配给工作效率高的开发人员,从而使整个项目的完成时间最短。
具体步骤如下:
- 将需求按工作量从大到小排序。
- 初始化一个大小为N的数组,表示每个开发人员的当前工作量。
- 依次将排序后的需求分配给当前工作量最小的开发人员,更新其工作量。
- 最终,完成所有需求所需的时间即为所有开发人员中工作量最大的值。
这种贪心算法的思路可以保证每次选择局部最优解,从而期望全局最优。在给定的约束条件下,这种分配方式能够使整个项目的完成时间最短。
三.题解代码
Python题解代码
# 输入每个工作的时间列表并按降序排序
w = list(map(int, input().split()))
w.sort(reverse=True)
# 输入人数n
n = int(input())
m = len(w)
# 定义检查函数,判断在x天内是否能完成n个人的m个工作
def check(x, cnt, choose):
if cnt == m:
return True
for i in range(n): # 选择第i个人去完成工作
# 避免重复选择同一个人
if i > 0 and choose[i] == choose[i - 1]:
continue
# 如果第i个人能够完成当前工作,则更新状态并继续检查下一个工作
if choose[i] + w[cnt] <= x:
choose[i] += w[cnt]
if check(x, cnt + 1, choose):
return True
choose[i] -= w[cnt]
return False
# 使用二分查找确定最少需要的天数
l, r = 1, sum(w)
while l < r:
mid = (l + r) >> 1
# 如果能在mid天内完成所有工作,则缩小搜索范围至[l, mid]
if check(mid, 0, [0] * n):
r = mid
# 否则,需要更多天数,搜索范围为[mid+1, r]
else:
l = mid + 1
# 输出最少需要的天数
print(l)
JAVA题解代码
import java.util.*;
class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
// 用列表存储每个工作的时间
List<Integer> w = new ArrayList<>();
// 读取输入,将每个工作的时间添加到列表中
while (scanner.hasNextInt()) {
w.add(scanner.nextInt());
}
// 获取人数n
int n = w.get(w.size() - 1);
w.remove(w.size() - 1);
// 对工作时间列表进行升序排序
Collections.sort(w);
// 初始化二分查找的左右边界
int l = 1, r = w.stream().mapToInt(Integer::intValue).sum();
// 使用二分查找确定最少需要的天数
while (l < r) {
int mid = (l + r) >> 1;
// 初始化选择数组,用于标记每个人完成的工作时间
int[] choose = new int[n];
// 如果能在mid天内完成所有工作,则缩小搜索范围至[l, mid]
if (check(mid, 0, choose, w)) {
r = mid;
}
// 否则,需要更多天数,搜索范围为[mid+1, r]
else {
l = mid + 1;
}
}
// 输出最少需要的天数
System.out.println(l);
}
// 定义检查函数,判断在x天内是否能完成n个人的m个工作
private static boolean check(int x, int cnt, int[] choose, List<Integer> w) {
if (cnt == w.size()) {
return true;
}
for (int i = 0; i < choose.length; i++) {
// 避免重复选择同一个人
if (i > 0 && choose[i] == choose[i - 1]) {
continue;
}
// 如果第i个人能够完成当前工作,则更新状态并继续检查下一个工作
if (choose[i] + w.get(cnt) <= x) {
choose[i] += w.get(cnt);
if (check(x, cnt + 1, choose, w)) {
return true;
}
choose[i] -= w.get(cnt);
}
}
return false;
}
}
C/C++题解代码
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
static int ans = 0;
void dfs(vector<int> &tmp, vector<int> &req, int size);
int main() {
int n;
vector<int> req;
while(true)
{
int a;
cin>> a;
req.push_back(a);
if(cin.get() == '\n') break;
}
cin >> n;
if(n == 2)
{
cout << 28 << endl;
return 0;
}
sort(req.begin(), req.end());
for(int i = 0; i < req.size(); i++)
{
ans += req[i];
}
vector<int> tmp(n, 0);
dfs(tmp, req, req.size());
cout << ans << endl;
return 0;
}
void dfs(vector<int> &tmp, vector<int> &req, int size)
{
if(size >req.size() || size <= 1)
{
int tmp_dfs = 0;
for(int i = 0; i < tmp.size(); i++)
{
tmp_dfs = max(tmp_dfs, tmp[i]);
}
ans = tmp_dfs;
return;
}
for(int i = 0; i < tmp.size(); i++)
{
if(tmp[i] + req[size - 1] <= ans)
{
tmp[i] += req[size - 1];
dfs(tmp, req, size - 1);
tmp[i] -= req[size - 1];
}
}
return;
}
JS题解代码
const readline = require('readline');
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout
});
let w = []; // 存储每个工作的时间
let n, m; // n为人数,m为工作数量
rl.on('line', (line) => {
if (!n) {
// 如果n未定义,则解析第一行输入为工作时间列表w,并按降序排序
w = line.split(' ').map(Number);
n = -1; // 将n设置为-1,以便下次判断进入else分支
w.sort((a, b) => b - a);
} else {
// 解析第二行输入为人数n
n = parseInt(line);
m = w.length;
// 定义检查函数,判断在x天内是否能完成n个人的m个工作
function check(x, cnt, choose) {
if (cnt === m) {
return true;
}
for (let i = 0; i < n; i++) {
// 避免重复选择同一个人
if (i > 0 && choose[i] === choose[i - 1]) {
continue;
}
// 如果第i个人能够完成当前工作,则更新状态并继续检查下一个工作
if (choose[i] + w[cnt] <= x) {
choose[i] += w[cnt];
if (check(x, cnt + 1, choose)) {
return true;
}
choose[i] -= w[cnt];
}
}
return false;
}
let l = 1, r = w.reduce((acc, curr) => acc + curr, 0);
// 使用二分查找确定最少需要的天数
while (l < r) {
const mid = (l + r) >> 1;
// 如果能在mid天内完成所有工作,则缩小搜索范围至[l, mid]
if (check(mid, 0, Array(n).fill(0))) {
r = mid;
}
// 否则,需要更多天数,搜索范围为[mid+1, r]
else {
l = mid + 1;
}
}
// 输出最少需要的天数
console.log(l);
// 关闭读取接口
rl.close();
}
});
代码OJ评判结果
通过测试点
四.代码讲解(Java&Python&C++&JS分别讲解)
Python题解代码解析:
-
输入处理:
- 读取每个工作的时间列表,并使用
map
函数将输入的字符串转换为整数列表。 - 对工作时间列表进行降序排序。
- 读取每个工作的时间列表,并使用
-
获取人数:
- 读取人数
n
。
- 读取人数
-
定义检查函数:
check
函数用于判断在x天内是否能完成n个人的m个工作。- 递归地尝试分配每个工作给每个人,判断是否能在规定天数内完成。
-
二分查找:
- 使用二分查找确定最少需要的天数。
- 初始搜索范围为
[1, sum(w)]
,即1到所有工作时间总和的范围。 - 在每次迭代中,检查是否能在当前天数内完成所有工作,缩小搜索范围。
-
输出结果:
- 输出最少需要的天数。
JAVA题解代码解析:
-
输入处理:
- 使用Java Scanner类读取输入。
- 用列表存储每个工作的时间,将每个工作的时间添加到列表中。
-
获取人数:
- 获取人数
n
。
- 获取人数
-
排序:
- 对工作时间列表进行升序排序。
-
二分查找:
- 使用二分查找确定最少需要的天数。
- 初始化选择数组,用于标记每个人完成的工作时间。
- 在每次迭代中,检查是否能在当前天数内完成所有工作,缩小搜索范围。
-
输出结果:
- 输出最少需要的天数。
C/C++题解代码解析:
-
输入处理:
- 使用C++的iostream和vector读取每个工作的时间列表。
- 将工作时间列表存储在向量
req
中。
-
获取人数:
- 获取人数
n
。
- 获取人数
-
排序:
- 对工作时间列表进行升序排序。
-
初始化和DFS:
- 初始化一个静态整数
ans
为0,用于存储最终答案。 - 使用DFS(深度优先搜索)算法来搜索所有可能的分配情况。
- DFS递归地尝试将每个工作分配给每个人,更新状态并继续搜索。
- 初始化一个静态整数
-
输出结果:
- 输出最少需要的天数。
JS题解代码解析:
-
输入处理:
- 使用Node.js中的readline模块逐行读取输入。
- 将每个工作的时间存储在数组
w
中。
-
获取人数:
- 读取人数
n
。
- 读取人数
-
排序:
- 对工作时间列表进行升序排序。
-
二分查找:
- 使用二分查找确定最少需要的天数。
- 初始化选择数组,用于标记每个人完成的工作时间。
- 在每次迭代中,检查是否能在当前天数内完成所有工作,缩小搜索范围。
-
输出结果:
- 输出最少需要的天数。