2022/1/22笔记背包问题

首先是背包分类的模板:

1、0/1背包:外循环nums,内循环target,target倒序且target>=nums[i];
2、完全背包:外循环nums,内循环target,target正序且target>=nums[i];
3、组合背包:外循环target,内循环nums,target正序且target>=nums[i];
4、分组背包:这个比较特殊,需要三重循环:外循环背包bags,内部两层循环根据题目的要求转化为1,2,3三种背包类型的模板

1、最值问题: dp[i] = max/min(dp[i], dp[i-nums]+1)或dp[i] = max/min(dp[i], dp[i-num]+nums);
2、存在问题(bool):dp[i]=dp[i]||dp[i-num];
3、组合问题:dp[i]+=dp[i-num];

这样遇到问题将两个模板往上一套大部分问题就可以迎刃而解
下面看一下具体的题目分析:

01背包问题

最初01背包问题

首先先了解一下原始背包问题的解题思路和代码:
最开始的背包问题是二维动态规划
#include<iostream>
#include<vector>
using namespace std;
int main()
{
	vector<int> value={1,5,9,8,5};
	vector<int> volume={1,5,9,8,5};
	       int  arr=15; 
	int len=value.size();
	vector<vector<int>> pd(len+1,vector<int>(arr+1,0));
	for(int i=1;i<=len;i++){
		for(int j=1;j<=arr;j++){
			if(j<volume[i-1]) pd[i][j]=pd[i-1][j];
			else pd[i][j]=max(pd[i-1][j],pd[i-1][j-volume[i-1]]+value[i-1]);
		}
	}
	for(int i=0;i<=len;i++){
		cout<<endl;
		for(int j=0;j<=arr;j++)
		cout<<pd[i][j]<<"  ";
	}
    cout<<pd[5][15];	
} 
二维代码可以进行优化,去除选取物品的那一层,简化为一维背包
 一维
状态定义:dp[j]表示容量为j的背包能放下东西的最大价值
#include<iostream>
#include<vector>
using namespace std;
int main()
{
	vector<int> value={1,5,9,8,5};
	vector<int> volume={1,5,9,8,5};
	       int  arr=15; 
	int len=value.size();
	vector<int> pd(arr+1,0);
	for(int i=1;i<=len;i++){
		for(int j=arr;j>=1;j--){
			if(j>=volume[i-1])  pd[j]=max(pd[j],pd[j-volume[i-1]]+value[i-1]);
		}
		for(int k=0;k<=arr;k++) cout<<pd[k];
		cout<<endl;
	}
	cout<< pd[15];
}

01背包分割等和子集

分割等和子集:判断是否能将一个数组分割为两个子集,其和相等
0-1背包存在性问题:是否存在一个子集,其和为target=sum/2,外循环nums,内循环target倒序
,应用状态方程2
class Solution {
public:
    bool canPartition(vector<int>& nums) {
        int len=nums.size();
        int sum=0,tar=0;
        for (int a:nums) sum+=a;
        if(sum%2!=0) return false;
        tar=sum/2;
        vector<bool>dp (tar+1,false);
        dp[0]=true;
        for(int i=1;i<=len;i++)
            for(int j=tar;j>=nums[i-1];j--)
            {
                dp[j]=dp[j]||dp[j-nums[i-1]];
            }
        return dp[tar];
    }
};

01背包目标和

 目标和:给数组里的每个数字添加正负号得到target
 数组和sum,目标和s, 正数和x,负数和y,则x+y=sum,x-y=s,那么x=(s+sum)/2=target
 0-1背包不考虑元素顺序的组合问题:选nums里的数得到target的种数,外循环nums,内循环target倒序,应用状态方程3
先把第1行就是i=0时没有数值输入的情况下的初始状态确定,然后看第一列容量为0时状态
而且pd[0]的状态一般不会变化j是从1到target
class Solution {
public:
    int findTargetSumWays(vector<int>& nums, int target) {
        int arr=0;
        for(int a:nums) arr+=a;
        if((arr+target)%2!=0||arr+target<0) return 0;
        arr=(arr+target)/2;
        vector<int>pd(arr+1,0);
        pd[0]=1;                        容量为(arr+target)/2,就是要在数组中找到让容量
        for(int i=1;i<=nums.size();i++) 为0的种类,使用容量为0加1
            for(int j=arr;j>=nums[i-1];j--)
            {
                pd[j]+=pd[j-nums[i-1]];
            }
        return pd[arr];
    }
};

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值