01背包,完全背包,多重背包模板

本文用的是DP,其中f[v]表示存储的是前i个物体放到容量v时的最大价值

一,0/1背包问题

详讲可以看
(https://blog.csdn.net/liusuangeng/article/details/38374405)
我是这看懂的

例题引入:
0/1背包
Description

给定n个物品和一个背包。物品i的重量是wi,其价值为vi,背包的容量为C。问:应该如何选择装入背包的物品,使得装入背包中物品的总价值最大?

Input

输入的第一行为测试样例的个数T,接下来有T个测试样例。

每个测试样例的第一行是物品个数n(1 ≤ n ≤ 3500)和背包容量C(C ≤ 13000),接下来n行,每行两个正整数 wi和 vi( wi ≤ 1000, vi ≤ 1000 ),分别表示第i件物品的重量 wi及其价值 vi。

Output
对应每个测试样例输出一行,只有一个整数,表示总价值的最大值。

Sample Input

2
1 2
1 1
2 3
2 3
2 4

Sample Output

1
4

一维滚动数组01背包模板:

#include<bits/stdc++.h>
using namespace std;
/*有n件物品和一个容积为C的背包。1 ≤ N ≤ 1000 ,   1 ≤ m ≤ 1000
第i件物品的重量w[i],价值是v[i]。*/
int main(void)
{
	int n,C;
	int w[1001]={0},v[1001]={0};
	int f[13001];//n,m太大了用二维超空间了,应该用滚动数组 f[容量+1] 
	int i,j,t;
	scanf("%d",&t);
	while(t--)
	{
		memset(f,0,sizeof(f));//本题要求是在“不超过”背包容量的情况下,最多能获得多少价值或收益 
		//则数组f[]初始化为0;
		//如果是 “恰好装满”背包的情况下,最多能获得多少价值或收益,则数组全初始话为负无穷,但是f[0]=0;
		scanf("%d %d",&n,&C);//n个物体,m的容量
		for(i=1;i<=n;i++)
		scanf("%d %d",&w[i],&v[i]);
		//01背包核心部分: 
		for(i=1;i<=n;i++)
		for(j=C;j>=w[i];j--)
		f[j]=max(f[j],f[j-w[i]]+v[i]);//max( 不挑选,挑选 );
		printf("%d\n",f[C]);
	}
	return 0;
}

以下是用二维数组的01背包模板:

#include<bits/stdc++.h>
using namespace std;
/*有n件物品和一个容积为C的背包。1 ≤ N ≤ 1000 ,   1 ≤ m ≤ 1000
第i件物品的重量w[i],价值是v[i]。*/
int inf=-999999999;
int f[1001][1001];//数组太大了,必须设在这里 
int main(void)
{
	int n,C;
	int w[1001]={0},v[1001]={0};
//	int f[1001][1001];//n,m太大了用二维超空间了,应该用滚动数组 f[容量+1] 
	int i,j;
	scanf("%d %d",&n,&C);//n个物体,m的容量
	/*for(i=0;i<=n;i++)
	for(j=0;j<=C;j++)
	f[i][j]=inf;
	for(i=0;i<=n;i++)
	f[i][0]=0;
	如果要求是在“恰好装满”背包容量的情况下,最多能获得多少价值或收益 
	则数组全初始话为负无穷,但是f[i][0]=0;*/ 
	//但本题是 “不超过”背包的情况下,最多能获得多少价值或收益,则数组f[i][j]初始化为0;
	memset(f,0,sizeof(f));
	for(i=1;i<=n;i++)
	scanf("%d %d",&w[i],&v[i]);
	//01背包核心部分: 
	for(i=1;i<=n;i++)
	for(j=0;j<=C;j++)
	if(j<w[i])
	f[i][j]=f[i-1][j];
	else
	f[i][j]=max(f[i-1][j],f[i-1][j-w[i]]+v[i]);//max( 不挑选,挑选 );
	printf("%d\n",f[n][C]);
	return 0;
}

分析:
时间复杂度为O(n*C),如果体积很大但是价值很小,很容易超内存就是另外叫超大背包的问题了。

二,完全背包

解析(https://www.cnblogs.com/Kalix/p/7622102.html)

我觉得讲的不错,重点我标出来
1.“看完这个问题,你也许会觉得这个不就是01背包的升级版吗,其实就是这样,完全背包问题与01背包问题的区别在于完全背包每一件物品的数量都有无限个,而01背包每件物品数量只有1个
所以说与它相关的策略已经不是只有取和不取这两种策略了,而是有取0件、取1件、取2件……等等很多种策略”
2.“即:将一种物品拆成多件物品。
我们现在dp每一个物品,dp出该种物品不同剩余容量下的最优解,他是以每1个为单位的。考虑是否在当前所有物品总数中添加一件新的该物品”
+在这里插入图片描述
例题引入:

超市搞活动(完全背包)

Description

某超市举行活动,凡参加活动的市民,可以领到一个容量为C的箱子。超市里面的商品任意挑选,每种商品可拿的个数也无限制,只要能装进这个箱子(不超出箱子的容量),就可以免费拿走。

Input

多测试用例。

每个测试用例第一行是两个正整数 C 和 N ,( 0 < C ≤ 10,000, 0 < N < 10,000 ),C 表示箱子的容量,N 表示超市里面商品的总数量。

接下来 N 行,每行两个正整数 Pi 和 Qi ( 0 < Pi < 1000 , Qi > 0 , 1 ≤ i ≤ N ) ,表示第i件商品的价值和体积。

Output

每个测试用例输出一行:你能够免费拿走的商品的最大价值总和。

Sample Input

4 2
7 3
4 2

Sample Output

8

只要把“不超过”背包容量”01背包模板核心for循环改成顺序(即j从0~C)且修改状态方程即可

完全背包和01价值背包问题的区别在于每一件物品的数量都有无限个,而01背包每件物品数量只有一个。
在递推公式时,需要加以改变:dp[i][j] = max(dp[i - 1][j], dp[i][j - weight[i]] + value[i]); 注意这里需要考虑放入一个物品i时还可能继续放入i,所以不能是dp[i][j] = max(dp[i - 1][j], dp[i-1][j - weight[i]] + value[i]);

现在呈上完全背包的代码

#include<bits/stdc++.h>
using namespace std;
int main(void)
{
	int n,C;
	int f[10001];
	int i,j,w[10001],v[10001];
	while(scanf("%d %d",&C,&n)!=EOF)
	{
		for(i=1;i<=n;i++)
		scanf("%d %d",&v[i],&w[i]);
		memset(f,0,sizeof(f));
		for(i=1;i<=n;i++)
		for(j=w[i];j<=C;j++)
		f[j]=max( f[j],f[j-w[i]]+v[i]);
		printf("%d\n",f[C]);
	}
	return 0;
}

三,多重背包

例题引入:

多重背包

Description

给定N种物品和一个容量为C的背包,第i种物品最多有 Mi 件可用,每件的重量是Wi,价值是Vi。问:将哪些物品装入背包可使这些物品的重量总和不超过背包容量,且价值总和最大。

Input

输入的第一行为测试样例的个数T,接下来有T个测试样例。

每个测试样例的第一行是物品种数N(1 ≤ N ≤ 100)和背包容量C(C ≤ 10000)。

接下来N行,每行三个正整数,Wi ,Vi 和 Mi ( Wi ≤ 10000, Vi ≤ 10000, Mi ≤ 10000 ),分别表示第i种物品的重量 Wi ,价值 Vi ,及个数 Mi 。

Output

对应每个测试样例输出一行,只有一个整数,表示装入背包的物品总价值的最大值。

Sample Input

1
2 8
4 100 2
2 100 4

Sample Output

400

多重背包和01背包、完全背包的区别:多重背包中每种物品的数量是给定的,可能不是一个,绝对不是无限个。
方法一:转化为01背包。
方法二:计算考虑k种物品承重限制为N时最大价值f[k][N]时,递推公式考虑两种情况,要么第 i 种物品一件也不放,就是f[i-1][j], 要么第 i 种物品放 k 件,其中 1 <= k <= (N/weight[i]),考虑这一共 k+1 种情况取其中的最大价值即为f[i][j]的值,即f[i][j] = max{f[i-1][j], (f[i-1][j-k*weight[i]]+kvalue[i])}。 这里为什么不能像完全背包一样直接考虑f[i][j-weight[i]]+value[i]呢?因为这样不容易判断第 i 种物品的个数是否超过限制数量 num[i]。
很遗憾当数据过大时,以上两种方法均超时。我们还有两种方法
1.二进制的优化
“二进制的优化
这是一个多重背包的模板,也是十分好用的一种模板,因为这个比直接拆除01 背包来做
要省些时间。这是为啥呢,首先先由我讲一下为什么能换成01 背包吧。
举个例子。假如给了我们 价值为 2,但是数量却是10 的物品,我们应该把10给拆开,要知道二进制可是能够表示任何数的,所以10 就是可以有1,2, 4,8之内的数把它组成,一开始我们选上 1了,然后让10-1=9,再选上2,9-2=7,在选上 4,7-4=3,
而这时的3<8了,所以我们就是可以得出 10由 1,2,4,3,来组成,就是这个数量为1,2,3,4的物品了,那么他们的价值是什么呢,是2,4,6,8,也就说给我们的价值为2,数量是10的这批货物,已经转化成了价值分别是2,4,6,8元的货物了,每种只有一件哎!!!!这就是二进制优化的思想。”
附上我提交的代码,我用方法1和2都超时了
二进制优化AC了

#include<bits/stdc++.h>
using namespace std;
struct node{
 int w;
 int v;
};
int main(void)
{
 int n,C;
 int f[10001];
 struct node wuping[20001];//一定小于2n;
 int k,t,i,j,count,w,v,m;
 scanf("%d",&t);
 while(t--)
 {
  count=0;
  memset(f,0,sizeof(f));
  scanf("%d %d",&n,&C);
  for(i=1;i<=n;i++){
  scanf("%d %d %d",&w,&v,&m);//重量 价值 数量 
  k=1;
  while(m-k>0)
  {
   m-=k; 
   wuping[++count].w =k*w;//重量翻倍 
   wuping[count].v =k*v;//价值翻倍 
   k*=2;//指数增长 
  }
  wuping[++count].w =m*w;//剩下不足指数的数m 
  wuping[count].v =m*v;//不足指数的 
  }
  //成功转换为01背包
  for(i=1;i<=count;i++)//总数变为count
  for(j=C;j>=wuping[i].w ;j--)//容量 
  f[j]=max( f[j],f[j-wuping[i].w ]+wuping[i].v ); 
  printf("%d\n",f[C]);//f[容量]
 }
 return 0;
}

2.单调队列优化
(https://www.cnblogs.com/shuaihui520/p/9043143.html)
…这里提到还提到了另外一种优化:“那为什么会有完全背包和01 背包的不同使用加判断呢?原因也很简单啊,当数据很大w[i]*num,大于背包的容纳量时,我们就是在这个物品中取上几件就是了,这就是完全背包,反而小于容纳量(w[i]*num<v)的就是转化为01背包来处理就是了,可以大量的省时间。”
就是打起来有点麻烦(比二进制优化好点,但是还是没单调队列优化省时)~~

  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 多重背包问题是指在给定容量和物品的价值和重量的情况下,如何最大限度地装入物品,使得总价值最大化的问题。它的模板是:给定N种物品和一个容量为V的背包,每种物品有无限件可用,每件物品的重量是w[i],其价值是v[i]。求解将哪些物品装入背包可使价值总和最大。 ### 回答2: 多重背包问题是一个经典的组合优化问题,它是在0/1背包问题的基础上进行了扩展。在多重背包问题中,每个物品可以被选择的次数不再是1次,而是有一个确定的上限k次(k>1)。我们需要选择一些物品放入背包中,使得它们的总体积不超过背包的容量,并且使得它们的总价值最大化。 要解决多重背包问题,可以使用动态规划的方法。首先,我们定义一个二维数组dp[i][j],其中i表示前i个物品,j表示背包的容量。dp[i][j]表示当只考虑前i个物品、背包容量为j时,能够获取的最大价值。然后,我们可以使用如下的状态转移方程来计算dp[i][j]的值: dp[i][j] = max(dp[i-1][j], dp[i-1][j-v[i]]+w[i], dp[i-1][j-2v[i]]+2w[i], ..., dp[i-1][j-kv[i]]+kw[i]) 其中,v[i]表示第i个物品的体积,w[i]表示第i个物品的价值,k表示第i个物品的可选次数。上述状态转移方程的意义是,我们可以选择不取第i个物品,或者分别取1次、2次、...、k次第i个物品,选择这些情况下的最大价值。 最后,我们可以通过遍历所有的物品和背包容量,计算出dp[n][m],其中n表示物品的个数,m表示背包的容量。dp[n][m]即为问题的解,表示只考虑前n个物品、背包容量为m时能够获取的最大价值。 综上所述,多重背包问题的解决方法是利用动态规划,通过定义状态转移方程和计算数组dp的值,找到问题的最优解。希望以上介绍对您有所帮助。 ### 回答3: 多重背包问题是常见的背包问题之一,与0-1背包问题和完全背包问题类似,但有一些区别。 在多重背包问题中,给定n个物品和一个容量为V的背包,每个物品有两个属性:重量w和价值v。同时,每个物品还有对应的个数限制c,表示该物品的数量最多可以选择c次。 我们需要选择物品放入背包,使得背包的总容量不超过V,同时物品的总价值最大。 多重背包问题可以用动态规划来解决。 我们可以定义一个二维数组dp,其中dp[i][j]表示前i个物品中选择若干个物品放入容量为j的背包时的最大价值。 根据多重背包问题的特点,我们需要对每个物品的个数进行遍历,并依次判断放入背包的个数是否超过c。 具体的状态转移方程为: dp[i][j] = max(dp[i-1][j-k*w[i]] + k*v[i]),其中0 <= k <= min(c[i], j/w[i]) 最后,需要注意的是多重背包问题的时间复杂度较高,为O(N*V*∑(c[i])),其中N是物品的数量,V是背包的容量,∑(c[i])表示物品的个数限制的总和。 总结而言,多重背包问题是在0-1背包问题和完全背包问题基础上的一种更复杂的情况,需要对每个物品的个数进行遍历和判断,采用动态规划求解。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值