LindCode 92 · 背包问题----01背包问题

Source

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述



记忆化搜索–超时

  • 结束条件:枚举到第一个物品时
  • 返回值:返回枚举到当前物品时的最满状态
  • 本级递归做什么:计算当前物品放与不放入背包的结果,选择两个结果中最满的一种状态

背包问题||的思路很类似,这里就是把塞入物品的大小等同于它的价值,最满状态等同于塞入的最大价值,详情参考背包||

代码:

class Solution {
    
      
	map<pair<int,int>,int> cache;//缓存器---当前物品i,对应剩余空间j的状态下的最满状态
	vector<int> Size;
	int Cap;
public:
	int backPack(int m, vector<int>& A) 
	{
    
      
		Cap = m;
		Size = A;
		return dfs(A.size() - 1, m);
	}

	int dfs(int obj,int cap)
	{
    
      
		//1.递归结束条件:枚举到第一个物品时
		if (obj == 0)//如果当前对应背包容量大小能够放下第一个物品,那么最满状态就是第一个物品大小,否则为0
			return Size[obj]<=cap ? Size[obj] : 0;
		//当前对应状态结果已经求解出来了
		if (cache.find({
    
       obj,cap }) != cache.end()) return cache[{
    
      obj, cap}];
		//下面计算当前对应第i个物品背包容量为j下,求解背包最满状态
		//选
		int sel = 0;
		//看能不能放的下---如果放不下就是没选
		if (cap >= Size[obj])
		{
    
      
			sel = dfs(obj - 1, cap - Size[obj])+Size[obj];
		}
		int unsel = dfs(obj - 1, cap);
		return cache[{
    
      obj, cap}] = max(sel, unsel);
	}
};

在这里插入图片描述
当前记忆化搜索版本超时


DFS第二种思路—同样超时

化成一颗二叉树去思考
在这里插入图片描述
代码:

class Solution {
    
      
	map<pair<int,int>,int> cache;//缓存器---当前物品i,对应剩余空间j的状态下的最满状态
	vector<int> Size;
	int Cap;
    int MAX=INT_MIN;//记录背包最满容量
public:
	int backPack(int m, vector<int>& A) 
	{
    
      
		Cap = m;
		Size = A;
		//如果所有的物品重量总和都不超过背包大小,直接返回所有物品总重量
		int maxSum = accumulate(A.begin(), A.end(), 0);
		if (maxSum <= m) return maxSum;
		//给物品大小的数组进行排序
		sort(Size.begin(), Size.end());
		dfs(0, m);
		return MAX;
	}
	//obj: 当前物品   cap: 背包剩余容量
	int dfs(int obj,int cap)//返回当前背包最满容量
	{
    
      
		//结果已经计算出了,就直接返回
		if (cache.find({
    
       obj,cap }) != cache.end()) return cache[{
    
      obj, cap}];
		//当前背包剩余容量为0,刚好塞满
		if (cap == 0)
			return  cache[{
    
      obj, cap}]=Cap;
		//超过当前背包容量
		if (cap < 0)
			return cache[{
    
      obj, cap}]=Cap - cap - Size[obj - 1];
		//所有物品放入背包后,依旧有剩余空间
		if (obj>=Size.size()&&cap > 0) return cache[{
    
      obj, cap}]=Cap - cap;
		//因为当前物品大小已经按从小到大进行排序,如果下一个物品放不进去,就只走不放入下一个物品的路线
		int sel = 0;
		if (cap >= Size[obj])
		{
    
      
			sel = dfs(obj+1, cap-Size[obj]);
		}
		int unsel = dfs(obj + 1, cap);
		return MAX = max(sel, unsel);
	}
};

在这里插入图片描述


对两种DFS的总结

第一种递归其实遵照的是动态规划的思路,属于自下而上的递归
第二种递归是将问题转化为一个二叉树遍历的思路,属于自上而下的递归


动态规划

还是参考背包||的动态规划解法,这里基本与其思路一致,这里可以把每个物品的大小就看成每个物品的价值,而对应的最满状态看做背包塞入物品的最大价值

代码:

class Solution {
    
      
public:
	int backPack(int m, vector<int>& A)
	{
    
      
		// 如果背包容量或者物品数量为0,则直接返回
		if (m == 0 || A.size() == 0) return 0;
		int n = A.size();
		vector<vector<int>> dp(n, vector<int>(m + 1, 0));
		//初始化第一行
		for (int i = 0; i < m + 1; i++)
		{
    
      
			//枚举到当前背包容量如果可以放下第一个物品,那么最满状态等于第一个物品的大小
			dp[0][i] = i >= A[0] ? A[0] : 0;
		}

		//有了第一件物品的所有状态,我们就可以根据第一件物品的所有状态求解出第二件物品的所有状态....第三件,第四件....第n件
		//即有了二维数组第一行所有值后,就可以挨个求出第二行所有值,,,,第n行
		for (int i = 1; i < n; i++)//枚举每一个物品
		{
    
      
			for (int j = 0; j < m + 1; j++)//计算列----从0到m依次计算当前物品对应每一个背包容量的状态下的最满状态
			{
    
      
				//当前物品不放入背包
				int unsel = dp[i - 1][j];
				//选择当前物品----前提是背包剩余容量能够放下这件物品
				int sel = j >= A[i] ? dp[i - 1][j - A[i]] + A[i] : 0;

				dp[i][j] = max(unsel, sel);
			}
		}
		return dp[n - 1][m];
	}
};

在这里插入图片描述


滚动数组优化–dp[2][C+1] 解法

还是参考背包||对应解法,这里基本与其思路一致,这里可以把每个物品的大小就看成每个物品的价值,而对应的最满状态看做背包塞入物品的最大价值

代码:

class Solution {
    
      
public:
	int backPack(int m, vector<int>& A)
	{
    
      
		// 如果背包容量或者物品数量为0,则直接返回
		if (m == 0 || A.size() == 0) return 0;
		int n = A.size();
		vector<vector<int>> dp(2, vector<int>(m + 1, 0));
		//初始化第一行
		for (int i = 0; i < m + 1; i++)
		{
    
      
			//枚举到当前背包容量如果可以放下第一个物品,那么最满状态等于第一个物品的大小
			dp[0][i] = i >= A[0] ? A[0] : 0;
		}

		//有了第一件物品的所有状态,我们就可以根据第一件物品的所有状态求解出第二件物品的所有状态....第三件,第四件....第n件
		//即有了二维数组第一行所有值后,就可以挨个求出第二行所有值,,,,第n行
		for (int i = 1; i < n; i++)//枚举每一个物品
		{
    
      
			for (int j = 0; j < m + 1; j++)//计算列----从0到m依次计算当前物品对应每一个背包容量的状态下的最满状态
			{
    
      
				//当前物品不放入背包
				int unsel = dp[(i - 1)&1][j];
				//选择当前物品----前提是背包剩余容量能够放下这件物品
				int sel = j >= A[i] ? dp[(i - 1)&1][j - A[i]] + A[i] : 0;

				dp[i&1][j] = max(unsel, sel);
			}
		}
		return dp[(n - 1)&1][m];
	}
};

在这里插入图片描述


dp[C+1] 解法

还是参考背包||对应解法,这里基本与其思路一致,这里可以把每个物品的大小就看成每个物品的价值,而对应的最满状态看做背包塞入物品的最大价值

代码:

class Solution {
    
      
public:
	int backPack(int m, vector<int>& A)
	{
    
      
		// 如果背包容量或者物品数量为0,则直接返回
		if (m == 0 || A.size() == 0) return 0;
		int n = A.size();
		vector<int> dp(m+1,0);
		for (int i = 0; i < n; ++i)
		//还是是滚动行数组的概念,从第一行开始计算,
	//计算第二行时,因为会用到第一行前面的数据,因此从第一行尾端往前端进行覆盖
		{
    
      
			for (int j = m; j >= A[i]; --j)
			{
    
      
				int sel = dp[j];
				int unsel = dp[j - A[i]] + A[i];
				dp[j] = max(sel, unsel);
			}
		 }
		return dp[m];
	}
};

在这里插入图片描述