您的位置:首页 > 职场人生

程序员笔试题---01背包问题

2017-03-31 16:03 288 查看

引言

前几天笔试网易,题目比华为难度高,但是其实都很常规,主要还是自己练习太少,很多题目都只是粗浅的看过,并没有理解其内涵。比如网易家这道关于双核CPU任务调度的,实际上就是01背包问题,当时记得见过这类问题,但是没有想到是01背包问题,不过就算想到了,我一时也不能马上写出来。因此,现在重新总结01背包问题,避免以后遇到类似问题没有头绪。

01背包问题

对于n个重量分别为wj的食物,有一个容量为K的背包,问能否从n个食物中选择一部分将背包完全塞满?(或问背包最多能装走多重的食物?)。可以利用01背包问题解决的类似问题有:如何将一个数组划分为两个数组,且这两个数组的元素和相等?

问题分析

假设p(n,K)代表n个食物且背包重量为K的问题的解,那么我们可以将问题分为两种情况:

若第n个食物是问题最优解的一部分,则p(n,K)=p(n−1,K−wn)

否则,p(n,K)=p(n−1,K)

这样我们只需要计算所有p(i,k)…k∈[1,K],i∈[0,n]即可。

初始条件容易获得p(i,0)=true…i∈[0,n]p(0,k)=false…k∈[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];
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: