https://leetcode-cn.com/problems/find-minimum-time-to-finish-all-jobs/
思路一:显然最大工作时间满足单调性,所以可以用二分来写。但是每次
c
h
e
c
k
check
check我们只能暴力递归,这样会超时……所以要加上一些剪枝操作……这里简单说一下吧,任务要从耗时多的开始分配,如果第一次分配都没有找到可行解,那么一定没有可行解(该次递归下),如果最优分配没有可行解,那么一定没有可行解(该次递归下)。
class Solution {
public:
vector<int> workers;
bool dfs(vector<int>& jobs,int idx,int limit)
{
if(idx>=jobs.size())
return 1;
for(int &worker:workers)
{
if(worker+jobs[idx]<=limit)
{
worker+=jobs[idx];
if(dfs(jobs,idx+1,limit))
return 1;
worker-=jobs[idx];
// 最优分配 或 第一次分配
if(worker+jobs[idx]==limit||worker==0)
return 0;
}
}
return 0;
}
int minimumTimeRequired(vector<int>& jobs, int k) {
sort(jobs.begin(),jobs.end(),greater<int>());
workers.resize(k);
int l=jobs[0],r=0,mid;
for(int ele:jobs)
r+=ele;
while(l<=r)
{
mid=(l+r)>>1;
fill(workers.begin(),workers.end(),0);
if(dfs(jobs,0,mid))
r=mid-1;
else
l=mid+1;
}
return l;
}
};
思路二:状压
d
p
dp
dp,考虑给定的
n
n
n个工作,若第
i
i
i个工作已经分配了,我们认为
s
t
a
t
e
i
=
1
state_i=1
statei=1,否则认为
s
t
a
t
e
i
=
0
state_i=0
statei=0,那么可以用一个整数
s
t
a
t
e
state
state表示
n
n
n个工作的状态(分配)。不妨设
d
p
i
j
dp_{ij}
dpij表示到第
i
i
i个工人且工作分配状态为
j
j
j时的最小的工作时间,我们可以得到转移方程:
d
p
i
,
j
=
m
i
n
(
d
p
i
,
j
,
m
a
x
(
d
p
i
−
1
,
k
,
c
o
s
t
j
−
k
)
)
dp_{i,j}=min(dp_{i,j},max(dp_{i-1,k},cost_{j-k}))
dpi,j=min(dpi,j,max(dpi−1,k,costj−k))
其中
k
k
k是状态
j
j
j的子集,
c
o
s
t
j
−
k
cost_{j-k}
costj−k表示工作分配状态为
j
−
k
j-k
j−k时所需要的时间总和。如何枚举状态
j
j
j的子集?用位运算实现即可,我们可以令
k
=
j
k=j
k=j,每次循环令
k
=
(
k
−
1
)
&
j
k=(k-1)\&j
k=(k−1)&j,当
k
=
0
k=0
k=0跳出循环即可,需要注意的是,这种方式没有计算空集,不过在本题中没有影响。
class Solution {
public:
int minimumTimeRequired(vector<int>& jobs, int k) {
int times=1<<jobs.size();
vector<int> cost(times);
for(int i=1;i<times;i++)
{
int j=1,idx=0;
while(j<=i)
{
if(i&j)
cost[i]+=jobs[idx];
++idx;
j<<=1;
}
}
vector<vector<int>> dp(k,vector<int>(times,0x3f3f3f3f));
for(int i=0;i<times;i++)
dp[0][i]=cost[i];
for(int i=1;i<k;i++)
{
for(int j=0;j<times;j++)
{
int MIN=0x3f3f3f3f;
for(int k=j;k;k=(k-1)&j)
MIN=min(MIN,max(dp[i-1][k],cost[j-k]));
dp[i][j]=MIN;
}
}
return dp[k-1][times-1];
}
};