多重背包 记忆化搜索、动态规划、二进制优化、单调队列优化

题目描述

有N种物品和一个容量为V的背包。第i种物品最多有n[i]件可用,每件费用是w[i],价值是c[i]。求解将哪些物品装入背包可使这些物品的费用总和不超过背包容量,且价值总和最大
W:物品重量
V:物品价值
C:背包容量
N:物品数量

1. 记忆化搜索

和完全背包一样,只不过在循环结束的时候增加了一个退出条件

int dp[60][60];
int maxPrix(vector<int>& W, vector<int>& V, vector<int>& N, int C, int i)
{
	if (dp[i][C] >= 0) return dp[i][C];
	if (i == W.size()) return 0;
	int rs = 0;
	for (int n = 0; n * W[i] <= C && n <= N[i]; n++) // 和完全背包相比多增加了一个退出条件n < N[i]
	{
		int nextC = C - n * W[i];
		int sumV = n * V[i];
		rs = max(rs, maxPrix(W, V, N, nextC, i + 1) + sumV);
	}
	dp[i][C] = rs;
	return rs;
}

注: 要看完全背包戳我

2. 动态规划

和递归版没啥区别,就是照着递归改过来的

int numberBackpack1(vector<int>& W, vector<int>& V, vector<int>& N, int C)
{
	int n = W.size();
	vector<vector<int>> maxpix(n + 1, vector<int>(C + 1, 0)); // maxpix[i][j]表示物品为i背包剩余容量为j时的最大价值
	for (int i = 1; i <= n; i++) // 物品
	{
		for (int j = 1; j <= C; j++) // 背包容量
		{
			for (int n = 0; n * W[i - 1] <= j && n <= N[i - 1]; n++) // 增加了一个退出条件
			{
				int nextC = j - n * W[i - 1];
				int sumV = n * V[i - 1];
				maxpix[i][j] = max(maxpix[i][j], maxpix[i - 1][nextC] + sumV); 
			}
		}
	}
	return maxpix[n][C];
}

3. 二进制优化

优化也就是将第三个循环给去掉,但是怎么优化呢?
我们可以想办法把某种物品拆分成个数不等的若干份,而这些若干份把它当做一个新的品种,该品种的W,V,N三个参数都会得到更新,然后把W和V重新保存到一个新的数组里,其中N始终为1,呵呵,想想! N为1了不就是01背包问题了吗?

int numberBackpack(vector<int>& W, vector<int>& V, vector<int>& N, int C)
{
	// 将每种背包拆分成单个数量不等的新背包 - 之后就可以用01背包的方式求解
	vector<int> w, v;
	for (int i = 0; i < N.size(); i++)
	{
		int c = N[i];
		for (int k = 1; k <= c; k <<= 1)
		{
			v.push_back(V[i] * k);
			w.push_back(W[i] * k);
			c -= k;
		}

		if (c > 0)
		{
			v.push_back(V[i] * c);
			w.push_back(W[i] * c);
		}
	}
	int n = w.size();
	vector<int> maxpix(C + 1, 0);
	for (int i = 0; i < n; i++) // 物品
	{
		for (int j = C; j >= 0; j--) // 背包容量
		{
			if(w[i] <= j)
				maxpix[j] = max(maxpix[j - w[i]] + v[i], maxpix[j]);
		}
	}
	return maxpix[C];
}

4. 单调队列优化

待更。。。

你可能感兴趣的:(算法)