1723-完成所有工作的最短时间

题目

1723. 完成所有工作的最短时间 - 力扣(LeetCode) (leetcode-cn.com)

思路

为了求得最短的工作时间,我们需要给每个工人分配适当的任务。我们从最后一个工人开始考虑,该工人共计有 2 n 2^n 2n种工作分配方案( n n n为工作数量),从不分配任何任务到分配所有任务。当我们确定该工人的一种分配方案后,就需要给剩余的工人分配剩余的任务,显然,给剩余的工人分配剩余的任务是给所有工人分配所有任务的一个相同类型的子问题,我们可以考虑使用动态来解决。

根据上述分析,我们需要解决的问题为在总任务为t,总工人数为p时,需要的最短工作时间。我们使用二维数组dp[p][t]来记录该时间。然而,我们的任务是一个数组而不是一个整数,因此难以使用整数t来表示。为了使用整数t来表示任务,我们需要给所有可能的子任务编一个号,同时还需要保证各个子任务间能进行运算加减运算(这样才能从总任务里减去已经分配的子任务),为了实现这个功能,我们使用一个比特位来表示一个任务单元,比特位为1表示包含对应位置数组中的任务单元,为0则表示不包含对应位置数组中的任务单元。

          低比特位                       高比特位
			 0   1   2   3   4   5  ... 31
unsigned int 0   0   0   0   0   0  0 0 0
arr         {w0, w1, w2, w3, w4, w5, ...}                                            

根据题目描述,我们最多需要12位来记录所有的子任务(jobs数组最长长度为12),因此使用int是合适的。

为了求dp[p][t],我们需要遍历工人p的所有可能分配方案(0~t),求得在所有方案中用时最短的方案。

在给工人p分配任务cur时,由前p个工人完成任务t所需的最短时间为max(dp[p-1][t-cur], work_time(cur)),其中work_time(cur)用于求当前工人(1个人)完成任务cur所用时间的函数,work_time

cur取值范围为0~t,我们遍历所有的cur,求得前p个工人完成任务t所需的最短时间即为dp[p][t]

dp[p][t] = min ( 
	for cur in [0, t]:
		max(dp[p-1][t-cur], work_time(cur)
)

对于只有一个工人的情况(初始状态),有dp[0][cur]=work_time(cur),因此,后续的work_time(cur)可以使用dp[0][cur]来代替,避免重复计算。

代码

class Solution {
public:
    int minimumTimeRequired(vector<int>& jobs, int k) {
        // mask为低jobs.size()位为1的整数,mask + 1为所有的可能的子任务数量
        unsigned int mask = ((0x1 << jobs.size()) - 1);
        int dp[12][4096];

        for(int j = 0; j <= mask; j++) {
            dp[0][j] = work_time(jobs, j);
        }

        for (int i=1; i < k; i++) {
            // 前i+1个工人完成不同的子任务j所需的最短时间
            for(int j = 0; j <= mask; j++) {
                int tmp = INT_MAX;
                // 第i个工人分配任务p时,前i+1个工人完成子任务j所需的最短时间
                for(int p=j; p > 0; p--) {
                    p = p & j;
                    // 注意有dp[0][p] = work_time(jobs, p);
                    // 求第i个工人分配不同的任务时整体方案的最短时间
                    tmp = min(tmp, max(dp[0][p], dp[i-1][j ^ p]));
                }
                dp[i][j] = tmp;
            }
        }
        return dp[k-1][mask];
    }

    int work_time(vector<int>& jobs, unsigned int work) {
        int i = 0;
        int result = 0;
        while(work != 0) {
            if ( (work & 0x1) != 0) {
                result += jobs[i];
            }
            i++;
            work >>= 1;
        }
        return result;
    }
};

tips

使用vector作为二维数组的性能很差(比vector一维数组差)(vecotor作为一维数组性能也比静态一维数组差),下面的提交中172ms的使用普通静态二维数组,而500+~1000+ms则是使用vector作为二维数组的提交结果。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-T9Bk00au-1620489853260)(.\image\image-20210508225535281.png)]

你可能感兴趣的:(leetcode,算法,动态规划)