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

例题:
详讲可以看
(https://blog.csdn.net/liusuangeng/article/details/38374405)
我是这看懂的
在这里插入图片描述
模板:

#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[1001];//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;
}

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

#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;
}

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

只要把“不超过”背包容量”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]);

可参考
多重背包
多重背包
多重背包和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背包来处理就是了,可以大量的省时间。”
就是打起来有点麻烦(比二进制优化好点,但是还是没单调队列优化省时)~~

猜你喜欢

转载自blog.csdn.net/qq_43791377/article/details/87998750