01背包及其推广 实验报告(含代码)

一、01背包

有N件物品和一个最多能背重量为W 的背包。第i件物品的重量是weight[i],得到的价值是value[i] 。每件物品只能用一次,求解将哪些物品装入背包里物品价值总和最大——这就是标准的背包问题。

每一件物品只有两个状态,取或者不取,用0和1表示,就成了01背包。解决背包问题的基本思路分为三步:(这里不考虑暴力解法)

  1. 确定状态变量(函数)
  2. 确定状态转移方程(递推关系)
  3. 确定边界

具体实例如下。

物品编号1234
重量2345
价值3458

对于本题,解决思路如下:

1.确定状态变量

创建状态变量dp[i][j],表示前i件物品放入容量为j的背包所能取到的最大价值。

2.建立递推关系

物品i/容量j012345678
0000000000
10
20
30
40

当背包容量为0时,所能取到的最大价值均为0;当物品编号为0(即未放入物品时),所能取到的最大价值也均为0.

物品i/容量j012345678
0000000000
1003333333
20
30
40

现在i变为1,即开始放入编号为1的物品。由于其重量为2,只有背包容量大于等于2时才会产生价值(为3)。

物品i/容量j012345678
0000000000
1003333333
2003447777
30
40

现在i变为2,即同时放入编号为1(w=2)和2(w=3)的物品。只有在背包容量大于等于3时,才可以放入编号2的物品;大于等于5时,才可以同时放入比编号1和2的物品。

下面开始建立递推关系。

分为两种情况:

1.背包当前容量小于第i件物品的重量

此时第i件物品无法放入,那么在背包容量为j的情况下,此时背包能装下的最大容量就是放入第i-1件物品的情况,即dp[i][j]=dp[i-1][j]。

2.背包当前容量大于等于第i件物品的重量。

此时我们可以装入第i件物品,很自然地,我们会考虑装入第i件物品是赚还是不赚,即比较装入与不装入第i件物品,哪个选择带来的利益更大。

如果选择不装入第i件物品,则此时dp[i][j]=dp[i-1][j];

反之,dp[i][j]=dp[i-1][j-w[i]]+v[i],即,我们选择要装入第i件物品,就会占据w[i]的空间,式子的含义是在减去占据空间的前提下,装入第i-1件物品所能获得的最大价值,再加上第i件物品的价值(装入第i件物品)。

由此,公式得出,dp[i][j]=max(dp[i-1][j],dp[i-1][j-w[i]]+v[i])

物品i/容量j012345678
0000000000
1003333333
2003447777
3003457899
40034588912

最后,我们输出dp[N][W],也就是在装入N件物品、背包容量为W的情况下所能携带的最大利润。

3.具体代码实现

#include <iostream>
using namespace std;

#define N 4 // 物品容量
#define W 8 // 背包容量

int w[N+1] = {0,2,3,4,5};    // 重量
int v[N+1] = {0,3,4,5,8};    // 价值

int dp[N+1][W+1];    // 在携带第i件物品的容量为j的情况下背包的最大价值

int main()
{
    for(int i = 1; i <= N; i++)
    {    // 第i件物品
        for(int j = 1; j <=W; j++)
        {
            if(j < w[i])
            {    // 背包当前容量小于第i件物品的重量
                dp[i][j] = dp[i-1][j];
            }
            else
            {    // 背包当前容量大于等于第i件物品的重量
                dp[i][j] = max(dp[i-1][j],dp[i-1][j-w[i]] + v[i]);
            }
        }
    }

    cout << dp[N][W];

    return 0;
}

注:此部分参考博客【0-1背包问题 】详细解析+图解+详细代码-CSDN博客

二、01背包推广

既然是推广,那么大体思路都是一致的。多了一个限制条件,我们就多设一个维度的数组。

设dp[i][j][k]表示在考虑前i种物品,背包重量限制为j,体积限制为k时,可以获得的最大价值。

按照跟01背包类似的思路,可以总结出以下几个步骤来解决这个问题:

1.初始化数组

在没有物品以及背包容量为0时,显然其最大价值也为0,即dp[0][j][k](没有物品)= dp[i][0][0](容量为0)= 0。

2.分情况讨论

对于每个物品i来说(i从1到n),对于每个可能的背包重量j(j从0到W),对于每个可能的背包体积k(k从0到V),我们考虑两种情况,即装入与不装入。

1.不装入第i件物品

在重量与体积条件保持不变的情况下(即还是j和k),最大价值就是装入第i-1件物品时的价值,即dp[i][j][k]=dp[i-1][j][k]。

2.装入第i件物品

分为装得下和装不下。

如果装得下,需要满足两个条件,即重量wi不超过j,体积ci不超过k,同时还需要跟不装第i件物品时的最大价值进行比较(详细见01背包),即dp[i][j][k]=max(dp[i-1][j][k],dp[i-1][j-wi][k-ci]+vi)。

如果装不下,那么最大价值就与不装入一样,即dp[i][j][k]=dp[i-1][j][k]。

3.输出答案

在经过三层循环遍历后,最终dp[n][W][V]就是我们求得的最大价值。

下面给出具体的代码实现。

#include<iostream>
using namespace std;

#define MAXN 100
#define MAXW 1000
#define MAXV 1000
// 假设最大物品数量为100,背包最大重量为1000,最大体积为1000

int w[MAXN], c[MAXN], v[MAXN];	// 重量,体积,价值
int dp[MAXN + 1][MAXW + 1][MAXV + 1];	// 定义dp数组

int main()
{
	int n, W, V;	// 物品数量,背包重量,背包体积
	cin >> n >> W >> V;	// 输入
	for (int i = 1; i <= n; i++)
	{
		cin >> w[i] >> c[i] >> v[i];
	}	// 输入第i件物品的重量,体积和价值

	for (int i = 1; i <= n; i++)
	{	// 第i件物品
		for (int j = 1; j <= W; j++)
		{	// 重量
			for (int k = 1; k <= V; k++)
			{	// 体积
				// 先对数组进行初始化
				if (i == 0 || j == 0 || k == 0)
				{	// 物品件数,背包重量,背包体积有一个为0,其最大价值就是0
					dp[i][j][k] = 0;
				}
				// 再开始装入物品
				// 如果装得下
				else if (w[i] <= j && c[i] <= k)
				{	// 重量不大于背包重量,体积不大于背包体积
					dp[i][j][k] = max(dp[i - 1][j][k], dp[i - 1][j - w[i]][k - c[i]] + v[i]);
					// 比较是装还是不装,哪个价值更高
				}
				// 如果装不下
				else
				{
					dp[i][j][k] = dp[i - 1][j][k];
				}
			}
		}
	}

	// 循环完毕,输出结果
	cout << dp[n][W][V];
	
	return 0;
}

  • 22
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值