首先是背包分类的模板:
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]; } };