面试常见-01背包问题

  1. 背包最大承受能力为w,总商品个数为n,每件商品的重量各不相同,如何能让背包内的商品总重量达到最大值?(回溯背包)
    代码:
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
void dfs(vector<int> &vec,int w,int curw,int i,int n,int &res) {
	//如果物品个数达到最大值,或者当前总重量达到能承受最大值,保存并终止
	if(n==i || w>=curw)
	{
		res = max(res,curw);
		return;
	}
	//装第i个商品(如果装上后超过了最大承受力,忽略)
	if(curw+vec[i] <= w)
		dfs(vec,w,curw+vec[i],i+1,n,res);
	//不装第i个商品
	dfs(vec,w,curw,i+1,n,res);
}
int main()
{
	int w,n;
	cout<<"最大承受力"<<endl;
	cin>>w;
	cout<<"输入商品个数"<<endl;
	cin>>n;
	vector<int> vec;
	int weight;
	for(int i=0;i<n;i++)
	{
		cin>>weight;
		vec.push_back(weight);
	}
	int res = INT_MIN;
	dfs(vec,w,0,0,n,res);
	cout<<res<<endl;

	return 0;
}
  1. leetcode-416-分割等和子集
    题型:回溯、动态规划、0-1背包
    难度:中等
    题目:给定一个只包含正整数的非空数组。是否可以将这个数组分割成两个子集,使得两个子集的元素和相等。
    代码:(回溯背包法)
class Solution {
public:
    bool dfs(vector<int>& nums,int n,int i,int last){
        if(last == 0) return true;
        if(i >= n) return false;
        if(nums[i] == last) return true;
        if(nums[i] > last)//如果当前这个数比需要的数还大,不要直接false,越过它,往后看
        {
            return dfs(nums,n,i+1,last);
        }
        else//满足的话,选不选都看,有一个方案满足就可以
        {
            return dfs(nums,n,i+1,last-nums[i])||dfs(nums,n,i+1,last);
        }

    }
    bool canPartition(vector<int>& nums) {
        int n = nums.size();
        if(n < 2) return false;
        int sum = 0;
        for(int i=0;i<n;i++)
            sum += nums[i];
        //总和是奇数,false
        if(sum % 2 == 1) return false;
        //从大到小排序
        sort(nums.rbegin(),nums.rend());
        sum = sum/2;
        if(nums[0] > sum) return false;
        if(nums[0] == sum) return true;
        return dfs(nums,n,0,sum);
    }
};

代码(动态规划):

class Solution {
public:
    bool canPartition(vector<int>& nums) {
        int n = nums.size();
        if(n < 2) return false;
        int sum = 0;
        for(int i=0;i<n;i++)
            sum += nums[i];
        if(sum % 2 == 1) return false;
        sum /= 2;
        vector<vector<bool> > dp(n,vector<bool>(sum+1,false));
        //边界值
        if(nums[0] <= sum)
            dp[0][nums[0]] = true;
        for(int i=1;i<n;i++)
        {
            for(int j=0;j<=sum;j++)
            {
                if(nums[i] == j)
                    dp[i][j] = true;
                else if(nums[i] > j)
                    dp[i][j] = dp[i-1][j];
                else
                    dp[i][j] = dp[i-1][j] || dp[i-1][j-nums[i]];
            }
        }
        return dp[n-1][sum];
    } 
};
  1. leetcode-1049-最后一块石头的重量2
    题型:中等
    题目:有一堆石头,每块石头的重量都是正整数。每一回合,从中选出任意两块石头,然后将它们一起粉碎。假设石头的重量分别为 x 和 y,且 x <= y。那么粉碎的可能结果如下:
    如果 x == y,那么两块石头都会被完全粉碎;
    如果 x != y,那么重量为 x 的石头将会完全粉碎,而重量为 y 的石头新重量为 y-x。
    最后,最多只会剩下一块石头。返回此石头最小的可能重量。如果没有石头剩下,就返回 0。
    代码:
class Solution {
public:
    int lastStoneWeightII(vector<int>& stones) {
        int n = stones.size();
        if(n < 2) return 0;
        int sum = 0;
        for(int i=0;i<n;i++)
            sum += stones[i];
        int all = sum;
        sum = sum/2;
        vector<vector<int> > dp(n+1,vector<int>(sum+1,0));
        for(int i=1;i<=n;i++)
        {
            int cur = stones[i-1];
            for(int j=1;j<=sum;j++)
            { 
                dp[i][j] = max(dp[i][j],dp[i-1][j] );
                if( j >= cur)
                    dp[i][j] = max(dp[i][j],dp[i-1][j-cur]+cur );
                
            }
        
        }
        return all-2*dp[n][sum];
    }
};
  1. leetcode-494-目标和
    题型:01背包
    难度:中等
    题目:给定一个非负整数数组,a1, a2, …, an, 和一个目标数,S。现在你有两个符号 + 和 -。对于数组中的任意一个整数,你都可以从 + 或 -中选择一个符号添加在前面。
    返回可以使最终数组和为目标数 S 的所有添加符号的方法数。
    代码:
	//如何看出是01背包呢?在满足条件的一种方法里,所有元素里面,加号一组减号一组,假设加号组和为a,减号组和为b,a-b=S,a+b=Sum,转化为--->2*a=S+sum--->a=(S+sum)/2;那么如果S+sum是偶数,可以推断出,从所有元素中取元素放入背包,让其最大价值为(S+sum)/2, 在这种情况下,得到的方案数。       
class Solution {
    //dp[i][j]表示用数组内前i个元素,组成和为1的方案数
public:
    int findTargetSumWays(vector<int>& nums, int S) {
        int n = nums.size();
        if(n == 0 || S>1000) return 0;
                                                                vector<vector<int> > dp(n,vector<int>(2005,0));
        dp[0][nums[0]+1000] = 1;
        dp[0][1000-nums[0]] += 1;
        for(int i=1;i<n;i++)
        {
            for(int j=-1000;j<=1000;j++)
            {
                if(dp[i-1][j+1000] > 0)
                {
                    dp[i][j+1000+nums[i]] += dp[i-1][j+1000];
                    dp[i][j+1000-nums[i]] += dp[i-1][j+1000];
                }
            }
        }
        return dp[n-1][S+1000];
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值