• 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 };
  • 相关阅读:
    筛选法求素数
    C/C++经典面试题目
    操作系统笔试面试基本内容
    Win32/MFC的基本概念
    STL采用的标准模板库
    数据结构基本概念
    SQL基础语句
    C/C++基础概念
    计算机网络基础概念
    流水作业 批作业调度
  • 原文地址:https://www.cnblogs.com/habibah-chang/p/14435330.html
Copyright © 2020-2023  润新知