【学习笔记】——背包 之01背包


01背包:
01背包之所以叫做01背包,是因为对于每件物品,都只有两种情况:要或不要

例题:01背包

分析:每种物品只有两种情况:要或不要,典型的01背包
状态:f[i][v]:前 i 件物品放在 v 空间内产生的最大价值
推导方程:

当第 i 件物品放时:f[i][j]=f[i-1][j-w[i]]+c[i] (即:前 i-1 件物品放在 (j-第 i 件物品所占空间)时的最大价值 + 第 i 件物品的价值)
不放时:f[i]=f[i-1][j](即:前 i-1 件物品放在 j 空间中的最大值)
又因为要取最大价值,所以:f[i][j]=max(f[i-1][j-w[i]]+c[i],f[i-1][j])

#include
using namespace std;
int n,m;
int f[1000][1000];
int w[100100],c[100100];
int main()
{
	cin>>m>>n;
	for(int i=1;i<=n;i++)
		cin>>w[i]>>c[i];
	for(int i=1;i<=n;i++)
		for(int j=n;j>=1;j--)
			f[i][j]=max(f[i-1][j],f[i-1][j-w[i]]+c[i]);
	cout<

但这样的时间、空间复杂度均为O(n*v),其中时间复杂度已无法优化,但空间还可以优化

优化方法一:滚动数组

如果仔细看一下方程,不难发现,对于当前的f[i][j],其实只用到了第 i 行和第 i-1 行
所以,我们可以将f[n][m]定义为f[2][m](n,m均为长量),再定义两个变量:pre、cur( pre 表示该行的上一行,cur 表示该行)。
但我们怎么更改 pre 与 cur 的值呢?
非常简单:swap(cur,pre) 即可;

#include
using namespace std;
int n,m,cur=1,pre;
int w[100100],c[100100],f[1000][1000];
int main()
{
	cin>>n>>m;
	for(int i=1;i<=m;i++)
		cin>>w[i]>>c[i];
	for(int i=1;i<=m;i++)
	{
        swap(cur,pre);
		for(int j=n;j>=1;j--)
		{
			if(j>=w[i])
				f[cur][j]=max(f[pre][j],f[pre][j-w[i]]+c[i]);
			else 
				f[cur][j]=f[pre][j];
		}	
	}	
	cout<
优化方法二:

优化原理同上,不过比上一个更狠:直接压成一维

方程:f[j]=max(f[j],f[j-w[i]]+c[i])

解释:

在更新f[j]之前,f[j]相当于之前的f[i-1] [j](可以理解为:a=a+3;) 而更新后,f[j]就成了f[i] [j]

所以,必须从后往前更新,否则,方程就成了f[i][j]=max(f[i][j-w[i]]+c[i],f[i-1][v])

代码:

#include
using namesapce std;
int n,m;
int w[100100],c[100100];
int main()
{
    cin>>m>>n;
    for(int i=1;i<=n;i++)
        	cin>>w[i]>>c[i];
    for(int i=1;i<=n;i++)
        	for(int j=m;j>=1;j--)
                	if(j>=w[i])
                        	f[j]=max(f[j],f[j-w[i]]+c[i]);
    				else 
                        	f[j]=f[j-1];
    cout<