浅谈01背包和完全背包

01背包,基础背包问题之一。


具体问题是这样的。一个容量为V的背包,还有一些物品(每个物品只有一个),这些物品的体积w和价值p各不相同。求出能在不超过V的情况下尽可能的使价值最大。


对于01背包问题,用动态规划的思想很简单,只需考虑子问题的两种情况:
如果i个物品放入,那么就转化为前i-1个装入j-w[i],价值为dp[i-1][j-w[i]]+p[i]。
如果i个物品不放,那么就转化为前i-1个装入j,价值为dp[i-1][j]。


由此可以写出状态方程:
dp[i][j] = max(dp[i-1][j-w[i]] + p[i], dp[i-1][j]);


空间复杂度和时间复杂度均为O(VN)。
不过空间复杂度可以优化到O(V)。


二维数组简化为一维数组是一个很不错的优化方法。由上文可知,dp[i][j]是由dp[i-1][j-w[i]]+p[i]和dp[i-1][j]来决定的,所以可以用逆序来遍历内层循环。为什么要逆序呢?因为这样dp[i-1][j-w[i]]+p[i]和dp[i-1][j]所决定的dp[i][j]就是不包含第i个物品的了。常数上也可以优化一下。常数为什么可以这样优化?因为j-w[i]>=0嘛。。。剩下的自己想。。。


优化后的状态方程:
dp[j] = max(dp[j-w[i]]+p[i], dp[j]);


最终01背包问题核心代码就是这样:
for(int i = 1; i <= n; i++)
for(int j = V; j >= w[i]; j--)
dp[j] = max(dp[j-w[i]]+p[i], dp[j]);

下面附上01背包的模板:

#include 
#include 
using namespace std;
#define MAXL 30001
int dp[MAXL], V, n, w, p;

void ZeroOnePack(int w, int p);

int main()
{
	cin >> V >> n;
	for (int i = 1; i <= n; i++)
	{
		cin >> w >> p;
		ZeroOnePack(w, p);
	}
	cout << dp[V] << endl;
	return 0;
}

void ZeroOnePack(int w, int p)
{
	for (int j = V; j >= w; j--)
		dp[j] = max(dp[j - w] + p, dp[j]);
}



完全背包,基础背包问题之二。
具体问题是这样的。一个容量为V的背包,还有一些物品(每个物品有无限个),这些物品的体积w和价值p各不相同。求出能在不超过V的情况下尽可能的使价值最大。


对于完全背包问题,和01背包问题差不多,但01背包问题的决策是放与不放,而完全背包问题的决策是放0个、1个、2个……V/w[i]个。


根据01背包可写出状态方程:
dp[i][j] = max(dp[i-1][j-k*w[i]] + k*p[i], dp[i-1][j]);(0 <= k <= V/w[i])


时间复杂度为O(VN连加(V/w[i])),空间复杂度为O(VN)。
这种时间是很恐怖的,所以需要优化一下。


优化方面同样参考01背包,将二维数组简化为一维数组。至于简化方法,只需在01背包基础上将逆序改为正序即可。为什么这样做就行呢?可以想想当初为什么要将01背包逆序。因为逆序可以保证每种物品只用一次。如果正序的话,由dp[i-1][j-w[i]]+p[i]和dp[i-1][j]所决定的dp[i][j]就包含了第i种物品的情况,符合了完全背包的说法。空间复杂度也可以优化到O(V),和01背包思想一样,还有常数优化,这里不再赘述。


优化后的状态方程:
dp[j] = max(dp[j-w[i]]+p[i], dp[j]);
可以看出和01背包是一样的。


完全背包问题核心代码就是这样:
for(int i = 1; i <= n; i++)
for(int j = w[i]; j <= V; j++)
dp[j] = max(dp[j-w[i]]+p[i], dp[j]);

可以看出只有遍历顺序不同而已。


下面附上完全背包的模板:

#include 
#include 
using namespace std;
#define MAXL 30001
int dp[MAXL], V, n, w, p;

void CompletePack(int w, int p);

int main()
{
	cin >> V >> n;
	for (int i = 1; i <= n; i++)
	{
		cin >> w >> p;
		CompletePack(w, p);
	}
	cout << dp[V] << endl;
	return 0;
}

void CompletePack(int w, int p)
{
	for (int j = w; j <= V; j++)
		dp[j] = max(dp[j - w] + p, dp[j]);
}


你可能感兴趣的:(算法丶背包问题,c++,acm,动态规划)