1723. Find Minimum Time to Finish All Jobs

问题:

将一组jobs分配给k个人,求完成这些job,最小需要的时间。

Example 1:
Input: jobs = [3,2,3], k = 3
Output: 3
Explanation: By assigning each person one job, the maximum time is 3.

Example 2:
Input: jobs = [1,2,4,7,8], k = 2
Output: 11
Explanation: Assign the jobs the following way:
Worker 1: 1, 2, 8 (working time = 1 + 2 + 8 = 11)
Worker 2: 4, 7 (working time = 4 + 7 = 11)
The maximum working time is 11.
 
Constraints:
1 <= k <= jobs.length <= 12
1 <= jobs[i] <= 107

  

解法:Backtracking(回溯算法)DP(动态规划)

  • 状态:到当前job[pos]为止,job的分配情况path,以及当前需要的最大工数preMax
  • 选择:将当前job[pos]分配给 0~k-1 任意一个工人
    • 除去,这个工人当前所用工数,和已经选择过的工人相同。(这样的选择,没什么区别)
    • or,这个工人的工数+当前job[pos]的工数>res,超过已经成功分配得到的一个解。(我们要找更小的解)
  • 递归退出条件:pos==jobs.size

♻️ 优化:将jobs进行从大到小排序,先分配工数大的,会比较快。

相较于将 res 初始化为INT_MAX,更优化的方法:取最大的jobs.size/k个工作给一个工人,这个工人用时一定最大。

代码参考:

 1 class Solution {
 2 public:
 3     //state: So far, jobs assignment situation:path, and the max workLoad: preMax
 4     //jobs[pos] assign to who?
 5     //opt:workers[0]~wokers[k-1]
 6     int res = 0;
 7     int path[12];
 8     void backtrack(int pos, int preMax, vector<int>& jobs, int k) {
 9         if(pos == jobs.size()) {
10             res = min(res, preMax);
11             return;
12         }
13         unordered_set<int> used;
14         for(int i=0; i<k; i++) {
15             if(used.insert(path[i]).second==false) continue;//same workload try only once
16             if(path[i]+jobs[pos]>=res) continue;// bigger than already gotten min res.
17             path[i]+=jobs[pos];
18             backtrack(pos+1, max(preMax, path[i]), jobs, k);
19             path[i]-=jobs[pos];
20         }
21         return;
22     }
23     int minimumTimeRequired(vector<int>& jobs, int k) {
24         if(k==jobs.size()) return *max_element(begin(jobs), end(jobs));
25         vector<int> path(k, 0);
26         sort(jobs.begin(), jobs.end(), greater<int>());
27         for (int i = 0; i < jobs.size(); i += k)
28             res += jobs[i];
29         backtrack(0, 0, jobs, k);
30         return res;
31     }
32 };

DP:

状态:

dp[i][j]:i 个工人,做map=j的job,花费的最小工数。

状态转移:

将 i 个工人,分出一个人,去做 j 的所有子集合的可能,

例如其中一个子集合subj,那么这个人需要做的工数=sum[subj]

总工数:剩下i-1个人的工数dp[i-1][j-subj] , sum[subj] 中求最大。

那么,对于所有子集合可能,我们要求的最优解:min(所有可能):即:

dp[i][j] = min{ all subj: max( dp[i-1][j-subj], sum[subj] ) }

base case:

dp[1][j] = sum[j]

求sum[j]:

j为job的map选择:如果 j的某一位 i ==1,那么sum+=jobs[i],即:

for(all j : 0~(1<<n) )  i:第 i 个job

if ((j>>i)&1==1) sum[j]+=jobs[i]

代码参考:

 1 class Solution {
 2 public:
 3     //dp[i][j]:0~i workers to do map_j jobs: the min time cost
 4     //dp[i][j]=min( <for all subj> : max(dp[i-1][subj],sum[j-subj]) ) subj:subset of j
 5     //one worker to do sum[j-subj] and others have the min time cost: dp[i-1][subj];
 6     
 7     //base case: dp[1][j]=sum[job[0]+...+job[j]];
 8     int minimumTimeRequired(vector<int>& jobs, int k) {
 9         if(k==jobs.size()) return *max_element(begin(jobs), end(jobs));
10         int n = jobs.size();
11         vector<vector<int>> dp(k+1, vector<int>(1<<n));
12         vector<int> sum((1<<n), 0);
13         //get sum:
14         for(int i=1; i<(1<<n); i++) {
15             for(int j=0; j<n; j++) {
16                 if((i>>j)&1==1) {
17                     sum[i] += jobs[j];
18                 }
19             }
20         }
21         //base:
22         for(int j=0; j<(1<<n); j++) {
23             dp[1][j] = sum[j];
24         }
25         //loop:
26         for(int i=2; i<=k; i++) {//for every i workers
27             for(int j=1; j<(1<<n); j++) {//for each jobs assignment map:j
28                 dp[i][j] = max(dp[i-1][j],sum[0]);
29                 for(int subj=j; subj>0; subj=(subj-1)&j) {//for each subset of j:all->0
30                     dp[i][j] = min(dp[i][j], max(dp[i-1][subj], sum[j-subj]));
31                 }
32             }
33         }
34         
35         return dp[k][(1<<n)-1];
36     }
37 };
原文地址:https://www.cnblogs.com/habibah-chang/p/14435330.html