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

引言

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

01背包问题

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

问题分析

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

  • 若第n个食物是问题最优解的一部分,则 p(n,K)=p(n1,Kwn)
  • 否则, p(n,K)=p(n1,K)

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

初始条件容易获得

p(i,0)=truei[0,n]
p(0,k)=falsek[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];
            }

猜你喜欢

转载自blog.csdn.net/wutao1530663/article/details/68924609