首页 > 其他分享 >背包问题

背包问题

时间:2023-07-28 17:02:27浏览次数:26  
标签:背包 int max 问题 01 物品 include

引入

有 n 个物品和一个容量为 W 的背包,每个物品有重量 w{i}和价值 v{i}两种属性,要求选若干物品放入背包使背包中物品的总价值最大且背包中物品的总重量不超过背包的容量。


我们之后涉及到的所有背包问题都会根据这个背景展开



1. 01背包

每个物品只能选取一次。
这样每个物品都会只有两种状态 : 选与不选。用二进制表示就是0与1
也就是01背包

例题

有 n 种物品要放到一个袋子里,袋子的总容量为 m,第 i 种物品的体积为 vi,把它放进袋子里会获得 wi 的收益,每种物品至多能用一次,问如何选择物品,使得在物品的总体积不超过 m 的情况下,获得最大的收益?请求出最大收益。

输入格式
第一行两个整数 n,m。

接下来 n 行,每行两个整数 vi,wi。

输出格式
一个整数,表示答案。

样例输入
5 10
5 3
3 6
7 8
5 9
2 4
样例输出
19
数据规模
对于所有数据,保证 1≤n,m,vi,wi≤1000。


二维数组的解决方式

我们先来思考最直接的方式
设置一个dp数组 f[i][j] 代表从前i个物品里选,背包容量为j的时候的最大收益
对于每一个物品,都有选与不选两种方式。那么对于状态f[i][j]只可能是由两种状态转移过来的 : 选第i个物品的f[i-1][j-v[i]]+w[i]和不选第i个物品的f[i-1][j]


状态转移方程为:
          f[i][j] = max(f[i-1][j] , f[i-1][j-v[i]]+w[i])

代码如下

# include<bits/stdc++.h>

using namespace std;
typedef pair<int,int> pii;

pii a[1010];
int f[1010][1010];

int main()
{
	int n,m;cin>>n>>m;
	for(int i=1;i<=n;i++) cin>>a[i].second>>a[i].first;
	for(int i=1;i<=n;i++)
	{
		for(int j=0;j<=m;j++)
		{ 
			if(a[i].second>j) f[i][j] = f[i-1][j];
			else 
			{
				f[i][j] = max(f[i-1][j],f[i-1][j-a[i].second]+a[i].first);
			}
		}
	}
	cout<<f[n][m];
	return 0;
}

一维数组的解决方式

(图丑莫怪qwq)


我们可以看到在二维数组中f[i][j]是由他上面的元素和左上方的两个元素转移过来的

那么我们发现求一个元素其实只需要知道他上一行的特定的两个元素就行。对f[i]有影响的只有f[i-1]这一维,那我们将f[i-1]这一维拷贝到f[i]上完全可以变成
      f[i][j] = max(f[i][j] , f[i][j-v[i]]+w[i])
与其把f[i - 1]这一层拷贝到f[i]上,不如只用一个一维数组了,只用f[j](一维数组,也可以理解是一个滚动数组 。转移方程就变成了
      f[j] = max(f[j],f[j-v[i]]+w[i])

代码

# include<bits/stdc++.h>

using namespace std;
typedef pair<int,int> pii;

pii a[1010];
int f[1011];
int main()
{
	int n,m;cin>>n>>m;
	for(int i=1;i<=n;i++) cin>>a[i].first>>a[i].second;
	for(int i=1;i<=n;i++)
	{
		for(int j = m;j>=a[i].first;j--) //因为遍历物品是从前往后遍历的,如果j从前往后遍历的话,f[j]都是由考虑前i个物品的状态转移过来的,但我们希望的由考虑前i-1个物品的状态转移过来的,所以j从后往前遍历
		f[j] = max(f[j],f[j-a[i].first]+a[i].second);
	}
	cout<<f[m]<<endl;
	return 0;
}

01背包的状态转移方程和优化到这里就讲完了,其他背包问题都是基于这个问题展开的,以后的证明部分就不会像01这样讲的那么详细了

2. 完全背包

完全背包与 0-1 背包的区别仅在于一个物品可以选取无限次,而非仅能选取一次。
那么转移方程就变成了 f[i][j] = max(f[i-1][j],f[i][j-v[i]]+w[i]

二维数组代码

# include<bits/stdc++.h>

using namespace std;

int v[1111],w[1111],f[1111][1111];
int main()
{
	int n,m;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]) f[i][j] = f[i-1][j];
			else f[i][j] = max(f[i-1][j],f[i][j-v[i]]+w[i]);
		}
	}
	cout<<f[n][m]<<endl;
	return 0;
}

一维数组代码

# include<bits/stdc++.h>

using namespace std;

int f[1111],v[1111],w[1111];
int main()
{
	int n,m;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=v[i];j<=m;j++) f[j] = max(f[j],f[j-v[i]]+w[i]);
	}
	cout<<f[m];
	return 0;
}

3. 多重背包

多重背包也是01背包的一个变式。与01背包的区别在于每种物品有k[i]个,而非一个。
一个很朴素的想法就是把每个物品有k个看成01背包,k个物品只有取与不取两种


直接放代码

# include<bits/stdc++.h>

using namespace std;

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

考虑优化 我们想要让取0,1...n[i]件这个效率很低的枚举被更好的枚举方式所替代
也就是有更好的一组数,其数量比n[i]小,在这组数里取若干件可以代换n[i]这组数里的任何一个值 。 这也就是二进制枚举

下图来自dd大牛的背包九讲


代码

# include<bits/stdc++.h>

using namespace std;

int v[2222],w[2222],l[2222],f[2222];
int main()
{
	int n,m;cin>>n>>m;
	for(int i=1;i<=n;i++) cin>>v[i]>>w[i]>>l[i];
	for(int i=1;i<=n;i++)
	{
		int res = l[i];
		for(int k=1;k<=res;res-=k,k*=2)
		{
			for(int j=m;j>=v[i]*k;j--)
			{
				f[j] = max(f[j],f[j-v[i]*k]+w[i]*k);
			}
		}
		for(int j=m;j>=v[i]*res;j--) f[j] = max(f[j],f[j-v[i]*res]+w[i]*res);
	}
	cout<<f[m]<<endl;
	return 0;
}

4. 分组背包

有 n 件物品和一个大小为 m 的背包,第 i 个物品的价值为 w_i,体积为 v_i。同时,每个物品属于一个组,同组内最多只能选择一个物品。求背包能装载物品的最大总价值。


与01背包不同的是这个是每组只能选一个,那么对这个组进行01背包就好了

代码

# include<bits/stdc++.h>

using namespace std;
typedef pair<int,int> pii;


vector<pii> a[1010];
int f[1111];

int main()
{
	int n,m;cin>>n>>m;
	int cnt = 1;
	for(int i=0;i<n;i++)
	{
		int x,v,w;scanf("%d%d%d",&x,&v,&w);
		cnt = max(cnt,x);
		a[x].push_back({v,w});
	}	
	for(int i=1;i<=cnt;i++)
	{
		for(int j = m;j>=0;j--)
		{
			for(auto k:a[i])
			{
				
				if(j>=k.first) f[j] = max(f[j],f[j-k.first]+k.second);
			}
		}
	}
	cout<<f[m]<<endl;
	return 0;
}

关于这三层的遍历顺序的解释

第二层和第三层之所以不能调换的原因是一旦调换这个分组就会变成一个多重背包,变成了每组的物品全都可以取而不是只能取一个了

5. 二维费用背包

与01背包不同的是选一个物品会消耗两种价值(经费、时间),只需在状态中增加一维存放第二种价值即可。

例题

有 n 种物品要放到一个袋子里,袋子的总容量为 m,我们一共有 k 点体力值。第 i 种物品的体积为 vi,把它放进袋子里会获得 wi 的收益,并且消耗我们 ti 点体力值,每种物品只能取一次。问如何选择物品,使得在物品的总体积不超过 m 并且花费总体力不超过 k 的情况下,获得最大的收益?请求出最大收益。

输入格式
第一行三个整数 n,m,k。

接下来 n 行,每行三个整数 vi,wi,ti。

输出格式
一个整数,表示答案。

样例输入
5 10 10
3 8 3
4 10 2
5 1 5
1 10 4
4 4 1
样例输出
28
数据规模
对于所有数据,保证 1≤n,m,k,vi,wi,ti≤100。

代码

# include<bits/stdc++.h>

using namespace std;

int f[111][111];

struct object
{
	int v,w,t;
}a[111]; 


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

标签:背包,int,max,问题,01,物品,include
From: https://www.cnblogs.com/algoshimo/p/17581332.html

相关文章

  • Robot Framework 自动化测试部署常见问题及处理方法(一)
    1.在Python>>Scripts中运行pythonride.py时报错现象:1Traceback(mostrecentcalllast):2File“E:\Python27\lib\site-packages\robotide\application\application.py”,line56,inOnInit3self.frame=RideFrame(self,self._controller)4File“E:\P......
  • 【HarmonyOS】ArkTS 组件内转场动画,动画播放时颜色异常问题
    ​【关键字】HarmonyOS、ArkTS、组件内转场动画、颜色异常 【问题描述】根据组件内转场动画文档中示例编写代码,使用动画转场组件button,并给button设置背景色让button透明度为0,实现动画转场时,会先出现默认蓝色button,然后动画再消失,问题代码如下所示:@Stateflag:boolean=t......
  • 【HMS Core】统一扫描连续扫码、闪光灯关闭问题
    ​ 【问题描述1】使用DefaultViewMode进行扫码,如何实现连续扫码 【解决方案】在默认扫码模式DefaultView中,功能是集成在SDK内部的,无法设置连续扫码模式等信息。可以使用CustomizedViewMode这种模式,它提供了相关的api可以设置是否连续扫码:通过setContinuouslyScan方法......
  • 国标GB28181视频平台LntonGBS(源码版)国标视频平台大并发下SIP消息出现重复SN号的问题解
    随着国家倡导平安城市、智慧城市的建设,安防视频监控作为智慧城市安防建设的重要环节,也越来越受到重视。LntonGBS是基于公安部推出的安防主流协议(国标GB28181协议)的视频接入、处理及分发平台,具有视频直播监控、云端录像、云存储、检索回放、智能告警、语音对讲、平台级联等功能。我......
  • 【HarmonyOS】ArkTS 组件内转场动画,动画播放时颜色异常问题
    【关键字】HarmonyOS、ArkTS、组件内转场动画、颜色异常【问题描述】根据组件内转场动画文档中示例编写代码,使用动画转场组件button,并给button设置背景色让button透明度为0,实现动画转场时,会先出现默认蓝色button,然后动画再消失,问题代码如下所示:@Stateflag:boolean=true;@State......
  • 快捷搜索栏进行搜索,出现乱码问题
    参考修改以下内容: 图片中方法代码如下://[B72]HttpURLConnection garbled code,tiansheng 20230719 -b+       private static String getCharset(String type) {+               String[] typeArray = type.split(";");+       ......
  • Oracle问题:一张表推荐创建多少索引合适
     Oracle问题:一张表推荐创建多少索引合适 明确索引主要影响insert、delete以及索引字段的update情况下(还会占用空间,一般不考虑这点),结合需求:1.如果表基本静态,存储足够的情况下想建多少个都可以。2.字段如果null值占比大,对字段等值查询或者关联查询多也可以考虑,因为null不会去......
  • 高手算法专项训练-期望问题
    高手算法专项训练-期望问题T1猫抓老鼠​ 我们可以设猫在点\(u\)老鼠在\(v\)点时猫抓到老鼠的期望时间为\(f_{u,v}\),设此时猫的目标点为\(next_{u,v}\),而这个\(next_{u,v}\)很显然可以在跑\(n\)便BFS。注意\(f\)的转移顺序显然由\(u,v\)的距离来决定。所以......
  • 问题记录贴,留给以后的自己解决
    一、Python正则的一个小问题importrehtml="""<p>helloworld</p><p>anyiya1lake</p><p>anyiya2lake</p>"""last_name='lake'pattern=re.compile(f'<p>(.*?){last_name}</......
  • std::queue 中遇到释放内存错误的问题
    项目上有个需求要用到std::queue顺序处理消息事件简单的示例如下:structMyEvent{MyEvent(){event_=CreateEvent(nullptr,0,0,0);}~MyEvent(){std::cout<<"MyEventdeconstruct"<<std::endl;}voidRun(){if(event_!=nullptr){S......