Leetcode之完成所有工作最短的時間
技術標籤:leetcode和機試題leetcode
題目:
給你一個整數陣列 jobs ,其中 jobs[i] 是完成第 i 項工作要花費的時間。
請你將這些工作分配給 k 位工人。所有工作都應該分配給工人,且每項工作只能分配給一位工人。工人的 工作時間 是完成分配給他們的所有工作花費時間的總和。請你設計一套最佳的工作分配方案,使工人的 最大工作時間 得以 最小化 。
返回分配方案中儘可能 最小 的 最大工作時間 。
示例 1:
輸入:jobs = [3,2,3], k = 3
輸出:3
解釋:給每位工人分配一項工作,最大工作時間是 3 。
示例 2:
輸入:jobs = [1,2,4,7,8], k = 2
解釋:按下述方式分配工作:
1 號工人:1、2、8(工作時間 = 1 + 2 + 8 = 11)
2 號工人:4、7(工作時間 = 4 + 7 = 11)
最大工作時間是 11 。
程式碼:
方法一——狀態壓縮動態規劃:
class Solution { public: int minimumTimeRequired(vector<int>& jobs, int k) { int n = jobs.size(); vector<int> tot(1 << n, 0); for (int i = 1; i < (1 << n); i++) { for (int j = 0; j < n; j++) { if ((i & (1 << j)) == 0) continue; int left = (i - (1 << j)); tot[i] = tot[left] + jobs[j]; break; } } vector<vector<int>> dp(k, vector<int>(1 << n, -1)); for (int i = 0; i < (1 << n); i++) { dp[0][i] = tot[i]; } for (int j = 1; j < k; j++) { for (int i = 0; i < (1 << n); i++) { int minv = INT_MAX; for (int s = i; s; s = (s - 1) & i) { // 列舉 i 的全部子集 int left = i - s; int val = max(dp[j-1][left], tot[s]); minv = min(minv, val); } dp[j][i] = minv; } } return dp[k-1][(1<<n)-1]; } };
思路:
設jobs 的長度為 NN,則可以用一個 [0,2^N]之間的整數代表 jobs 的任意一個子集。下文中,我們不加區分地用 i 表示「整數 i」或者「整數 i 對應的子集」
於是,令 tot[i] 代表子集 i的工作總時間。設子集 i的其中(任意)一個元素為 j,則 i-(1<<j) 代表了「子集 i中去掉了元素 j 後剩下的那部分」。因此,我們有
tot[i]=tot[i−(1<<j)]+jobs[j]。
隨後,我們設 dp[j][i] 表示:前 j個工人為了完成作業子集 i,需要花費的最大工作時間的最小值。
由於前 j個工人完成了作業子集 i,因此第 j個工人必然完成 i的某個子集 s,而其餘的工人完成子集 i-s。對於特定的 s 而言,前 j−1 個工人完成子集 i−s 需要花費的最大工作時間的最小值為 dp[j-1][i-s],而工人 jj 的工作時間為 tot[s],故此時總的最大工作時間的最小值為 max{dp[j−1][i−s],tot[s]}。
因此,根據題意,我們遍歷所有的子集 s ,並求出全域性的最小值,即
dp[j][i]=s⊆imin(max{dp[j−1][i−s],tot[s]})
最終,dp[k-1][(1<<N)-1]即為我們所求的答案。
方法二——: