[状压dp]leetcode1986:完成任务的最少工作时间段(medium)
·
题目:


题解:
思路:状压 dp,枚举子集,学习枚举状态 i 子集的方法。由于 n<=20,可以很方便的使用一个 n 位二进制来表示每个数选与不选。
- 1)预处理 dp 数组,将[0,2^n-1]的所有二进制状态 s 中构成的子集的时间 <= sessionTime 的 dp[s] 初始化为 1。
- 2)进行状态转移方程,即每个状态 s 枚举其子集,更新最小的 dp[s],子集 j 可以从 s 开始枚举,也可以从 s&(s-1) 开始枚举,j 在 s 内的补集为 j ^ s ,然后我们更新 dp[s] 的话,就是取min(dp[s],dp[j]+dp[h ^ s])。
代码如下:
class Solution {
public:
int minSessions(vector<int>& tasks, int sessionTime) {
int n=tasks.size();
vector<int> f(1<<n,20);
// 枚举子集,[0,2^n-1]之间的所有二进制数,哪些位为1则在tasts中选取那些数字
for(int s=1;s<1<<n;++s){
int spend=0;
for(int i=0;i<n;++i){
// s的第i位为1,则选取这个数字
if(s>>i&1)spend+=tasks[i];
}
// 该小子集满足情况
if(spend<=sessionTime)f[s]=1;
}
// 开始对每个状态枚举子集
for(int s=1;s<1<<n;++s){
if(f[s]==1)continue;
for(int j=(s-1)&s;j>s>>1;j=(j-1)&s){
// j为s的子集,s^j=s1为j在s内的补集,即s1∪j=s
f[s]=min(f[s],f[j]+f[s^j]);
}
}
return f[(1<<n)-1];
}
};
更多推荐
所有评论(0)