01背包及滚动数组优化(一维)

01背包问题

  • 有 N 件物品和一个容量是 V 的背包。每件物品只能使用一次。
  • 第 i 件物品的体积是 vi,价值是 wi。
  • 求解将哪些物品装入背包,可使这些物品的总体积不超过背包容量,且总价值最大。 输出最大价值。

输入格式

  • 第一行两个整数,N,V,用空格隔开,分别表示物品数量和背包容积。
  • 接下来有 N 行,每行两个整数 vi,wi,用空格隔开,分别表示第 i 件物品的体积和价值。

输出格式

输出一个整数,表示最大价值。

数据范围

0 0

输入样例

4 5
1 2
2 4
3 4
4 5

输出样例:

8


代码

其中,初始化的部分也可以不写,因为数组定义在main函数外面,自动初始化为0了

#include
#define maxn 1010
#define max(a,b) a>b?a:b
int v[maxn],w[maxn];//v[i],w[i]分别表示第i件物品的体积和价值。
int dp[maxn][maxn];//dp[i][j]表示前i件物品,体积刚好为j时最大价值。 
int main()
{	
	int n,V;//n为物品数量,v为背包容积 
	scanf("%d%d",&n,&V);
	for(int i=1;i<=n;i++)
	{
		scanf("%d%d",&v[i],&w[i]);
	}
	for(int i=0;i<=n;i++)
	dp[i][0]=0;					//边界条件 
	for(int i=0;i<=V;i++)
	dp[0][i]=0; 				//边界条件 
	for(int i=1;i<=n;i++)		//i表示前i件 
	{
		for(int j=1;j<=V;j++)	//j表示体积刚好为j 
		{
			dp[i][j]=dp[i-1][j];
			if(j>=v[i])
			dp[i][j]=max(dp[i][j],dp[i-1][j-v[i]]+w[i]);
		}
	}
	int sum=0;
	for(int j=1;j<=V;j++)
	{
		sum=max(dp[n][j],sum);
	}
	printf("%d\n",sum);
	return 0;
}

对于01背包问题,需要dp[n][v]数组,即需要n*m大小的空间,如果遇到较大的数据就会被善良的出题人卡掉,那么如何进行优化呢?


#include
#define maxn 1010
#define max(a,b) a>b?a:b
int v[maxn],w[maxn];//v[i],w[i]分别表示第i件物品的体积和价值。
int dp[maxn];//dp[i][j]表示前i件物品,体积刚好为j时最大价值。 
int main()
{	
	int n,V;//n为物品数量,v为背包容积 
	scanf("%d%d",&n,&V);
	for(int i=1;i<=n;i++)
	{
		scanf("%d%d",&v[i],&w[i]);
	}
	for(int i=1;i<=n;i++)		//i表示前i件 
	{
		for(int j=V;j>=v[i];j--)	//j表示体积刚好为j 
		{
			dp[j]=max(dp[j],dp[j-v[i]]+w[i]);
		}
	}
	int sum=0;
	for(int j=1;j<=V;j++)
	{
		sum=max(dp[j],sum);
	}
	printf("%d\n",sum);
	return 0;
}

是不是很神奇,只需要把第一维全部砍掉,再把j的范围改为从后往前,j–。是什么原理呢


原理(滚动数组)


dp[i][j]=max(dp[i-1][j],dp[i-1][j-v[i]]+w[i])


注意到这个二维数组的下一行的值,只用到了上一行的正上方和左边的值,因此可用滚动数组的思想,只要一行即可。

既然是要用正上方和左边的值,那么从右边开始依次求值,并覆盖在正上方的位置即可
01背包及滚动数组优化(一维)_第1张图片
01背包问题题目链接

你可能感兴趣的:(动态规划)