01背包,完全背包

本文介绍了01背包问题的基本概念,探讨了贪心算法的局限性,通过动态规划求解全局最优解,并展示了如何使用二维数组和一维数组优化空间。同时,文章还提及了完全背包问题的区别,并提供了相应的优化代码示例。
摘要由CSDN通过智能技术生成

01背包

01背包问题  :

                现在有一个背包 背包容量为 m  还有n件物品,每件物品有自己的所占的容量 v  与价值  w ,每件物品只能取一次或者不取,求在背包容量为m时可取的最大价值

贪心的问题

在新手不知道01背包的话可能会实现一个这样的贪心  用  w/v  把性价比,进行降序排,然后顺着取,但是如果说现在这样

背包容量为 5  有  3件物品  第一件物品大小为  2 价值为  2   

物品编号容量(v)价值(w)
123
234
346

这样按照  w/v  性价比  算的话  先装性价比高的  能获取的最大价值为 6 ,但实际上这个背包能获取的最大价值时7。

上面是个典型的贪心,但是贪心算法总是一种局部最优解,而我们需要全局最优解

全局最优 -动态规划

下面是给出01背包问题的

现在背包容量为   7     有   4件物品

物品编号容量价值
122
234
345
467

现在,我们这样,假如背包容量为 1 对  前1件物品进行取舍,每件物品,只有两种可能,拿取或者不拿  ,那么我们现在设置一个dp数组

物品编号(看下方)|背包容量(看右边)1234567
00000000
1(2,2)0222222
2(3,4)
3(4,5)
4  (6 ,  7)

物品编号(看下方)|背包容量(看右边)1234567
00000000
1(2,2)0222222
2(3,4)0244666
3(4,5)
4  (6 ,  7)

我们对背包容量进行枚举,每一次判断这个物品是否可以放入,如果可以就对前i-1件物品,容量为j-v[i]是进行考量,达到每一个dp元素都是当前的最优解,达到全局最优的。

二维01背包板子

那么我们创建二维的dp数组,给出板子

#include<bits/stdc++.h>

using namespace std;
int n, m;//n 是物品数量 ,m是背包大小
int v[1010], w[1010];//   v是物品体积,w是物品价值
int dp[1010][1010];

signed main()
{ 
	cin >> n >> m;
	for (int i = 1; i <= n; i++)cin >> v[i] >> w[i];
	for (int i = 1; i <= n; i++)
	{
		for (int j = 0; j <= m; j++)
		{
			if (j >= v[i])//背包大小为j时,需要考虑此时放入第i个物品,当放入第i个物品时,此时背包体积为j时的最优解变成前i-1件物品在背包容量为j-v[i]时的最优解
				dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - v[i]] + w[i]);
			else
				dp[i][j] = dp[i - 1][j];  //第i件物品大于背包大小为j时,放不下,此时全局最优解时  背包大小为j,前i-1件物品时的最优解
		}
	}
	cout << dp[n][m];
	return 0;
}

当然这里还可以优化点空间,把dp数组压缩成一维数组,节省一点买不起的空间

优化一维

在上面状态转移方程时

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

如果说我们省略 i 也就是 件数  ,那么 状态转移方程该咋写呢

不讲废话 直接看板子

#include<bits/stdc++.h>

using namespace std;
int n, m;//n 是物品数量 ,m是背包大小
int v[1010], w[1010];//   v是物品体积,w是物品价值
int dp[1010];

signed main()
{ 
	cin >> n >> m;
	for (int i = 1; i <= n; i++)cin >> v[i] >> w[i];
	for (int i = 1; i <= n; i++)
	{
		for (int j = m; j >= v[i]; j--)
		{
				dp[j] = max(dp[j], dp[j - v[i]] + w[i]);
		}
	}
	cout << dp[n];
	return 0;
}

完全背包

完全背包问题

                现在有一个背包 背包容量为 m  还有n件物品,每件物品有自己的所占的容量 v  与价值  w ,每件物品能取任意次或者不取,求在背包容量为m时可取的最大价值。

问题探究

一个物品取得次数已经给出是无数次,我们不需要考虑这当前的物品(第i件)时对前面 前 i-1 件进行取舍 ,比如说现在正在取第i件物品,这件物品的容量为v[i],价值为 w[i] ,那么在容器的容积为x时 ,如果我们考虑放1件这个物品,那嘛,现在情况变成  在容积为  x-v[i]时对前i件物品的取舍,

也就是说  dp[i][x]=max(dp[i][x],dp[i][x-v[i]+w[i])  这个状态转移,我们在当前容积对前第i件物品,考虑时放几个  ,如果放1个就是  dp[i][x]=max(dp[i][x],dp[i][x-v[i]]+w[i]) ,如果放两个 

dp[i][x]=max(dp[i][x],dp[i][x-2*v[i]]+2*w[i])   当然前提是能放的下,相比于  01  背包,我们由于每次取舍,如果取就得变成在前i-1物品背包容量为x-v[i]。大家理解下这段话

j接下来时代码

#include<bits/stdc++.h>

using namespace std;
int n, m;//n 是物品数量 ,m是背包大小
int v[1010], w[1010];//   v是物品体积,w是物品价值
int dp[1010][1010];

signed main()
{
	cin >> n >> m;
	for (int i = 1; i <= n; i++)cin >> v[i] >> w[i];
	for (int i = 1; i <= n; i++)
	{
		for (int j = 0; j <= m; j++)
		{
			for (int k = 1; k * v[i] <= j; k++)  //k 是当前放入得个数  ,如果种物品放入的  个数*每个的容积<=当前背包的容积
			{
				dp[i][j] = max(dp[i][j], dp[i][j - k * v[i]] + k * w[i]);
			}
		}
	}
	cout << dp[n][m];
	return 0;
}

对比01背包的优化

当然这个也可以节省一点价值不菲的空间,同01背包,也是舍去当前的物品种类数。

现在我们从新回头看之前对01背包的优化

那么由上面对我们从前面往后面遍历即使完全背包,蒙b了吗(咳咳)

代码

#include<bits/stdc++.h>

using namespace std;
int n, m;//n 是物品数量 ,m是背包大小
int v[1010], w[1010];//   v是物品体积,w是物品价值
int dp[1010];

signed main()
{
	cin >> n >> m;
	for (int i = 1; i <= n; i++)cin >> v[i] >> w[i];
	for (int i = 1; i <= n; i++)
	{
		for (int j = 0; j <= m; j++)
		{
			for (int k = 1; k * v[i] <= j; k++)  //k 是当前放入得个数  ,如果种物品放入的  个数*每个的容积<=当前背包的容积
			{
				dp[j] = max(dp[j], dp[j - k * v[i]] + k * w[i]);
			}
		}
	}
	cout << dp[m];
	return 0;
}

  • 13
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值