【LintCode】—— 0-1背包问题

Backpack

题目描述

在n个物品中挑选若干物品装入背包,最多能装多满?假设背包的大小为m,每个物品的大小为A[i]

样例 1:

输入: [3,4,8,5], backpack size=10
输出: 9

样例 2:

输入: [2,3,5,7], backpack size=12
输出: 12

解题思路

这里使用动态规划的思想,动态规划四部曲

  • 状态:F(i,j) 将第i个物品装入体积为j的的背包中
  • 递推方程
    分两种情况:1.第i个物品的大小大于当前背包能装入物品的大小,放不下,
    此时第i个物品最大大小与放入第i - 1物品时相同 F(i,j) = F(i-1,j)
    2.第i个物品大小小于当前背包能装入物品大小,此时又分为
    两种情况:1.放的下不放该物品,此时和第一种情况一样F(i,j) = F(i-1,j)
    2.放的下且放进去,此时放入第i个物品的重量等于背包减去第i件物品对应大小对应位置上的背包装入的最大重量加上第i件物品的重量 F(i,j) = F(i - 1,j - A[i] + A[i]), 此时必须取上述两种情况中大的一个 max(F(i-1,j),F(i-1,j-A[i]+A[i]))
  • 初始化
    第1行和第1列的值都为0,因为未放置物品F(i,0) = F(0,j) = 0
  • 返回值
    返回将n个物品放入大小为m的背包中 F[n][m]

但是我们想一想,在求第i件物品装入之后所得最大的重量时我们用到的只有放入第 i-1 件时所得到的重量,所以我们这时优化一下我们代码的空间复杂度,只开辟一维数组的空间,就可以解决上述问题,但是这里必须注意的是,我们在更新的时候必须从后往前更新这个数组,因为我们要用到之前的数据,避免被新数据覆盖。

代码实现

class Solution {
public:
	/**
	* @param m: An integer m denotes the size of a backpack
	* @param A: Given n items with size A[i]
	* @return: The maximum size
	*/

	int backPack(int m, vector<int> &A) {
		// write your code here
		if (m < 1 || A.empty())
			return 0;
		int N = A.size();
		//多开辟一列,方便初始化第一列的值为0
		int M = m + 1;

		vector<int> Fv(M, 0);
		for (int i = 0; i < N; i++)
		{
			//从后往前更新,否则会覆盖之前的数据
			for (int j = M - 1; j > 0; j--)
			{
				//若第i件物品若能放进去,找放进去和不放进去中质量更大的那个
				//放进去,就是减去第i件物品大小之后,在Fv中对应位置的质量加上第i件物品的重量
				//不放进去就是一维数组该位置的质量
				if (A[i] < j)
					Fv[j] = max(Fv[j], Fv[j - A[i]] + A[i]);
				else
					//放不进去就是该位置的质量
					Fv[j] = Fv[j];
			}
		}
		return Fv[m];
	}
};

题目链接:Backpack

BackPackII

题目描述

有 n 个物品和一个大小为 m 的背包. 给定数组 A 表示每个物品的大小和数组 V 表示每个物品的价值.

问最多能装入背包的总价值是多大?

样例 1:

输入: m = 10, A = [2, 3, 5, 7], V = [1, 5, 2, 4]
输出: 9
解释: 装入 A[1] 和A[3] 可以得到最大价值, V[1] + V[3] = 9

样例 2:

输入: m = 10, A = [2, 3, 8], V = [2, 5, 8]
输出: 10
解释: 装入 A[0] 和 A[2]可以得到最大价值, V[0] + V[2] = 10

解题思路

动态规划:

  • 状态:F(i,j) 将第i个物品装入体积为j的的背包中
  • 递推方程:
    分两种情况:1.第i个物品的大小大于当前背包能装入物品的大小,放不下,
    此时第i个物品最大价值与放入第i - 1物品时相同 F(i,j) = F(i-1,j)
    2.第i个物品大小小于当前背包能装入物品大小,此时又分为
    两种情况:1.放的下不放该物品,此时和第一种情况一样F(i,j) = F(i-1,j)
    2.放的下且放进去,此时放入第i个物品的价值等于减去i对应大小后对应位置上的价值加上第i个物品对应的价值,F(i,j) = F(i - 1,j - A[i] + V[i]),此时必须取上述两种情况中大的一个 max(F(i-1,j),F(i-1,j-A[i]+V[i]))
  • 初始化:
    第1行和第1列的值都为0,因为未放置物品F(i,0) = F(0,j) = 0
  • 返回值:
    返回将n个物品放入大小为m的背包中 F[n][m]

代码实现

class Solution {
public:
	/**
	* @param m: An integer m denotes the size of a backpack
	* @param A: Given n items with size A[i]
	* @param V: Given n items with value V[i]
	* @return: The maximum value
	*/
	
	int backPackII(int m, vector<int> &A, vector<int> &V) {
		// write your code here
		if (A.empty() || V.empty() || m < 1)
			return 0;

        //增加一行一列以方便初始化
		int N = A.size() + 1;
		int M = m + 1;
		
		//将所有值先初始化为0,包括第一行第一列
		vector<vector<int>> Fv(N, vector<int>(M, 0));

		for (int i = 1; i < N; ++i)
		{
			for (int j = 1; j < M; ++j)
			{
				//当第i个物品装不下背包时,价值与之前的相等
				if (A[i - 1] > j)
					Fv[i][j] = Fv[i - 1][j];
				//装的下时,在装不装中选取更大的值
				else
				{
					//如果能够放进背包,那就在放进去和不放进去中找一个价值更大的一个
					//不放进去价值和之前该位置的值相等
					//放进去,等于减去i对应大小后对应位置上的价值加上第i个物品对应的价值
					Fv[i][j] = max(Fv[i - 1][j], Fv[i - 1][j - A[i - 1]] + V[i - 1]);
				}
			}
		}
		return Fv[N - 1][m];
	}
};

优化BackPack II

思路:
将第i个物品装入体积为j的的背包中
由于我们求上述问题时只需要去得放第i-1个物品时取得的最大价值
因此我们将二维空间优化为一维空间,必须从前往后更新这个一维数组,
否则在使用前面数据时之前的就已经被覆盖

class Solution {
public:
	int backPackII(int m, vector<int> &A, vector<int> &V) {
		// write your code here
		if (A.empty() || V.empty() || m < 1)
			return 0;

		int N = A.size();
		int M = m + 1;

		vector<int> Fv(M, 0);
		for (int i = 0; i < N; ++i)
		{
			for (int j = M - 1; j > 0; j--)
			{
				//如果第i个物品放不进背包,价值与之前一维数组中该位置的值相等
				if (A[i] > j)
					Fv[j] = Fv[j];
				else
				{
					//如果能够放进背包,那就在放进去和不放进去中找一个价值更大的一个
					//不放进去价值和之前该位置的值相等
					//放进去,等于减去i对应大小后对应位置上的价值加上第i个物品对应的价值
					Fv[j] = max(Fv[j], Fv[j - A[i]] + V[i]);
				}
			}
		}
		return Fv[m];

	}
发布了167 篇原创文章 · 获赞 175 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/chenxiyuehh/article/details/100066055