力扣周赛 1723 完成所有工作的最短时间
题意
给 \(n\) , \(k\) 和一个长度为 \(n\) 的数组 \(a\) ,将 \(a\) 分成 \(k\) 组,求 \(k\) 组里最大值的最小值
\(1 \le k \le n \le 12\)
思路
这道题目的做法有很多,比较正确的做法是状压dp,也可以用模拟退火来做,也可以用贪心瞎搞 A 过去
状压dp
设 f[j][i] 表示前 j 组完成了任务集合 i 的最大值的最小值
答案就是 f[k-1][(1 << n) - 1], 考虑其转移
枚举集合 i 的子集 s
\[f[j][i] = min_{s\in i}(max\{f[j-1][i-s], tot[s]\})
\]
class Solution {
public:
int tot[1<<13];
int f[13][1<<13];
int minimumTimeRequired(vector<int>& jobs, int k) {
memset(f,0x3f,sizeof f);
int n = jobs.size();
for(int i = 0;i < 1 << n;i++){
for(int j = 0;j < n;j++){
if((i >> j) & 1)tot[i] += jobs[j];
}
}
for(int i = 0;i < (1 << n);i++)f[0][i] = tot[i];
for(int j = 1;j < k;j++){
for(int i = 0;i < 1 << n;i++){
int minv = 0x3f3f3f3f;
for(int s = i;s ;s = (s - 1) & i){
minv = min(minv, max(f[j-1][i - s], tot[s]));
}
f[j][i] = minv;
}
}
return f[k-1][(1<<n) - 1];
}
};
模拟退火
参考 HAOI2006 均分数据, 改一改就可以了 , 我的代码
瞎搞
贪心的把目前的 \(A_i\) 加入到最小的 sum 中, 用 random_shuffle 瞎搞个几万次取最小值
class Solution {
public:
int cal(vector<int>& jobs, int k){
int sum[20] = {0};
for(int v : jobs){
int idx = 0,mx = 120000000;
for(int i = 1;i <= k;i++){
if(sum[i] < mx){
mx = sum[i];
idx = i;
}
}
sum[idx] += v;
}
int ans = 0;
for(int i = 1;i <= k;i++)ans = max(ans,sum[i]);
return ans;
}
int minimumTimeRequired(vector<int>& jobs, int k) {
int ans = 0x3f3f3f3f;
int t = 50000;
while(t--){
random_shuffle(jobs.begin(),jobs.end());
ans = min(ans, cal(jobs,k));
}
return ans;
}
};