【坚持每日一题5.8】1723. 完成所有工作的最短时间

本文探讨了一种使用动态规划和状态压缩技术解决工作分配问题的方法,目标是找到在k位工人间分配给定任务,使得最大工作时间最小化的最优策略。通过实例和代码实现,深入解析了如何计算并找到分配方案中的最小最大工作时间。
摘要由CSDN通过智能技术生成

给你一个整数数组 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 。

思路:

动态规划 + 状态压缩
思路及算法

按照朴素的思路,我们按顺序给每一个工人安排工作,注意到当我们给第 ii 个工人分配工作的时候,能够选择的分配方案仅和前 i-1i−1 个人被分配的工作有关。因此我们考虑使用动态规划解决本题,只需要记录已经被分配了工作的工人数量,以及已经被分配的工作是哪些即可。

因为工作数量较少,我们可以使用状态压缩的方式来表示已经被分配的工作是哪些。具体地,假设有 nn 个工作需要被分配,我们就使用一个 nn 位的二进制整数来表示哪些工作已经被分配,哪些工作尚未被分配,如果该二进制整数的第 ii 位为 11,那么第 ii 个工作已经被分配,否则第 ii 个工作尚未被分配。如有 33 个工作需要被分配,那么 5=(101)_25=(101)
2

即代表 第 00 和第 22 个工作已经被分配,第 11 个工作还未被分配。

这样我们可以写出状态方程:f[i][j]f[i][j] 表示给前 ii 个人分配工作,工作的分配情况为 jj 时,完成所有工作的最短时间。注意这里的 jj 是一个二进制整数,表示了工作的分配情况。实际上我们也可以将 jj 看作一个集合,包含了已经被分配的工作。

那么我们可以写出状态转移方程:

f[i][j] = \min_{j’\in j}{ \max(f[i-1][\complement_{j}j’], \textit{sum}[j’])}
f[i][j]=
j

∈j
min

{max(f[i−1][∁
j

j

],sum[j

])}

式中 \textit{sum}[j’]sum[j

] 表示集合 j’j

中的工作的总工作量,\complement_{j}j’∁
j

j

表示集合 jj 中子集 j’j

的补集。状态转移方程的含义为,我们枚举 jj 的每一个子集 j’j

,让其作为分配给工人 ii 的工作,这样我们需要给前 i-1i−1 个人分配 \complement_{j}j’∁
j

j

的工作。

在实际代码中,我们首先预处理出 \textit{sum}sum 数组,然后初始化 f[0][j]=sum[j]f[0][j]=sum[j],最终答案即为 f[k-1][2^n-1]f[k−1][2
n
−1](表示给全部 kk 个工人分配全部 nn 个工作,完成所有工作的最短时间)。

java代码:
class Solution {
    public int minimumTimeRequired(int[] jobs, int k) {
        int n = jobs.length;
        int[] sum = new int[1 << n];
        for (int i = 1; i < (1 << n); i++) {
            int x = Integer.numberOfTrailingZeros(i), y = i - (1 << x);
            sum[i] = sum[y] + jobs[x];
        }

        int[][] dp = new int[k][1 << n];
        for (int i = 0; i < (1 << n); i++) {
            dp[0][i] = sum[i];
        }

        for (int i = 1; i < k; i++) {
            for (int j = 0; j < (1 << n); j++) {
                int minn = Integer.MAX_VALUE;
                for (int x = j; x != 0; x = (x - 1) & j) {
                    minn = Math.min(minn, Math.max(dp[i - 1][j - x], sum[x]));
                }
                dp[i][j] = minn;
            }
        }
        return dp[k - 1][(1 << n) - 1];
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值