引言
前几天笔试网易,题目比华为难度高,但是其实都很常规,主要还是自己练习太少,很多题目都只是粗浅的看过,并没有理解其内涵。比如网易家这道关于双核CPU任务调度的,实际上就是01背包问题,当时记得见过这类问题,但是没有想到是01背包问题,不过就算想到了,我一时也不能马上写出来。因此,现在重新总结01背包问题,避免以后遇到类似问题没有头绪。
01背包问题
对于n个重量分别为
问题分析
假设
- 若第n个食物是问题最优解的一部分,则
p(n,K)=p(n−1,K−wn) - 否则,
p(n,K)=p(n−1,K)
这样我们只需要计算所有
初始条件容易获得
也就是说背包容重为0时,总是能够塞满背包;而物品为0且背包容重大于0时,总是不能塞满。
实际代码中,我们可以建立一个大小为n*K的表格记录下所有p(i,k)(如果不需要知道中间过程,可以只使用2*K空间, 也就是两个数组分别存储p(i - 1,k)的数据和p(i,k),每次完成一次记录后交换数组即可。)。如下所示为leetcode中一道类似的题目解答:
leetcode 416. Partition Equal Subset Sum
class Solution {
public:
bool canPartition(vector<int>& nums) {
int sum = accumulate(nums.begin(),nums.end(),0);
if(sum&1) return false;
sum >>=1;
int size= nums.size();
vector<int> col1(sum + 1);
vector<int> col2(sum + 1);
col1[0] = 1;
for(int i = 0;i < size;++i){
for(int k = 0;k <= sum;++k){//根据col1更新col2
if(col1[k])//如果p(i - 1,k) = 1,则p(i,k) = 1
col2[k] = col1[k];
else if(k >=nums[i])//否则p(i,k) = p(i-1,k - nums[i])
col2[k] = col1[k-nums[i]];
}
col2.swap(col1);//交换数组
}
return col1[sum];//返回p(n,sum)
}
};
如果问题是问背包中最多能装多少食物。那么p(i,k)的记录就不应该只是简单的true or false 而是k背包能从i个食物中能装的最大值。
...
if(k >=nums[i])//如果k能容纳nums[i]则p(i,k)取p(i - 1,k-nums[i])+nums[i] 和 p(i -1,k)的最大值
col2[k] = max(col1[k-nums[i]] + nums[i],col1[k]);
else //否则就取
col2[k] = col1[k];
}