01背包问题算是动态规划里经典中的经典了,没学过的同学之前应该也有所耳闻。江湖老规矩,先来描述一下什么是01背包问题。假设你有一个背包,最多能承重C千克,这里有k个物品,其重量分别为w1、w2、……、wk,其价值分别为v1、v2、……、vk,在背包所能承受的重量下,尽可能得使背包里的价值最大。(注意,该物品只能放或者不放,不能只放该物品的0.8这样子,非0即1,故称为01背包问题)此问题理解起来不难,那下面直接看题。


0-1背包问题 (转自PTA)

给定一个承重量为C的背包,n个重量分别为w1​,w2​,...,wn​的物品,物品i放入背包能产生pi​(>0)的价值(i=1,2,...,n)。
每个物品要么整个放入背包,要么不放。要求找出最大价值的装包方案。

输入格式:

输入的第一行包含两个正整数n和C(1≤n≤20),第二行含n个正整数分别表示n个物品的重量,第三行含n个正整数分别表示n个物品放入背包能产生的价值。

输出格式:

在一行内输出结果,包括最大价值装包方案的价值、具体装包方案,用空格隔开。具体装包方案是n个物品的一个子集,用长度为n的0、1串表示(1表示对应物品被选中,0表示没有被选中)。如果这样的0、1串不唯一,取字典序最大的那个串。

输入样例:

4 9
2 3 4 5
3 4 5 7

输出样例:

12 1110

(注:1110 和0011都是价值最大的装包方案,取字典序最大的结果即为1110)


输出格式中有两个问题,第一是求出背包的最大价值,第二是价值最大的装包方案

问题一:求出背包的最大价值

for(int i=1;i<=n;i++)//物品i 
{
	for(int j=1;j<=c;j++)//重量j 
	{
		if(j>=w[i])
		{
			arr[i][j]=max(arr[i-1][j],arr[i-1][j-w[i]]+v[i]);	
		}
		else arr[i][j]=arr[i-1][j];
	}
}

我们先来理一下思路。面对这么多的物品,我们可以选择一个个地来装入背包,背包的承重量也一点点的提升,直至C(设立二维数组,列为物品i,行为背包的承重量)。假设当物品 i 的重量大于背包当前的总承重时,毋庸置疑,该物品不能放入背包;当物品 i 的重量小于背包的总承重时,我们就要进行对比,前面 i - 1个物品所带来的价值和现在要取出背包中的一部分物品用来存放物品i带来的价值,哪个更大?(取出多少呢,当然是刚好能放下物品 i 的重量,即w[ i ]),把更大的那个价值对当前背包价值进行更新。那么现在我们就能得出一个背包最大价值更新的公式:

arr[ i ][ j ] = max(arr[ i - 1 ][ j ], arr[ i - 1][ j - w[ i ] ] + v[ i ]),还不理解的同学看下图,参考题目的样例(看下面讲解的时候注意样例中对应的重量和价值):

此二维数组是从上到下,从左到右看,arr[ i ][ j ]代表的是前 i 个物品在背包承重为 j 的时候的最大价值。先来看看物品1那一行是怎么得出来的?只有物品1的时候,无论背包承重多少,价值最大只能为3;再看看arr[ 3 ][ 7 ],当背包重量为7时,因为arr[ 2 ][ 7 ]<arr[ 3 ][ 7 - 4 ] + 5,所以选择放入物品3。(如果选择不放入物品3时,前2个物品在背包重量为7的时候,价值为7,而当选择放入物品3时,价值为9,所以把arr[ 3 ][ 7 ]更新为9),其他的原理一样。求解得到的背包的最大价值是arr[ n ][ C ],即前n个物品在背包最大承重为C的时候的最大价值。

问题二:打印价值最大的装包方案

int h=n,g=c;
while(h>=1)
{
	if(arr[h][g]==arr[h-1][g])flag[h]=0;
	else 
	{
		flag[h]=1;
		g=g-w[h];
	}
    h--;
}
for(int i=1;i<=n;i++)
{
	printf("%d",flag[i]);
}

理解该背包问题所求得的二维数组后,打印就不成问题了。

利用回溯法,从arr[ n ][ C ]开始回溯,当arr[ n ][ C ] == arr[ n - 1 ][ C ]时,说明有没有物品n都一样,即物品n没有放入背包中;当arr[ n ][ C ] != arr[ n - 1 ][ C ]时,说明物品n已经放入背包中,造成了背包价值的变化,物品 - - ,背包的重量也变成了C - w[ n ],逐一回溯。

小细节:题目中要求输出字典序最大的结果,我们在求解最大价值的时候,也是按照前面的物品先放入背包,当且仅当后面物品放入的时候产生影响也考虑放入,所以我们回溯的时候得出的结果就是本题需要的答案。

ac代码如下:

#include<iostream>
#include<algorithm>
using namespace std;
int w[21]={0};
int v[21]={0};
int flag[21]={0};
int arr[21][1024]={0};
int main()
{
	int n=0,c=0;
	scanf("%d%d",&n,&c);
	for(int i=1;i<=n;i++)
	{
		scanf("%d",&w[i]);
	}
	for(int i=1;i<=n;i++)
	{
		scanf("%d",&v[i]);
	}
	for(int i=1;i<=n;i++)//物品i 
	{
		for(int j=1;j<=c;j++)//重量j 
		{
			if(j>=w[i])
			{
				arr[i][j]=max(arr[i-1][j],arr[i-1][j-w[i]]+v[i]);	
			}
			else arr[i][j]=arr[i-1][j];
		}
	}
	printf("%d ",arr[n][c]);
	int h=n,g=c;
	while(h>=1)
	{
		if(arr[h][g]==arr[h-1][g])flag[h]=0;
		else 
		{
			flag[h]=1;
			g=g-w[h];
		}
		h--;
	}
	for(int i=1;i<=n;i++)
	{
		printf("%d",flag[i]);
	}
	return 0;
}

 希望能帮助到大家!

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐