首页 > 其他分享 >动态规划之背包问题

动态规划之背包问题

时间:2024-02-27 22:46:28浏览次数:11  
标签:背包 int 1010 max 物品 动态 规划 dp

动态规划之背包问题

作为算法界的经典,背包问题一直是动态规划的一个代表,也是给了无数算法新人一记迎头痛击啊,我也是被其困扰了好长一段时间,连最基础的模板都很难理解,更别说无尽的变体了,今天我就来带大家回顾一下基础模板的思路。

01背包

题面:

N 件物品和一个容量是 V 的背包。每件物品只能使用一次。

i件物品的体积是 v[i],价值是 w[i]

求解将哪些物品装入背包,可使这些物品的总体积不超过背包容量,且总价值最大。

输出最大价值。

代码:

int main()
{
    int n, m, v[1010] = { 0 }, w[1010] = { 0 }, dp[1010]= { 0 };
    scanf("%d %d", &n, &m);
    for (int i = 1; i <= n; i++) scanf("%d %d", &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]);
    printf("%d", dp[m]);
    return 0;
}

题解:

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

考虑前i个物品在空间为j的情况下的最优解,要加入当前物品需要预留空间,则将当前与腾出空间后加上当前物品价值对比得出当前最优解。

值得注意的是第二层j循环从大到小遍历直到不可能装下当前物品,若从小到大则可能会发生前面的情况影响后面的情况,导致一件物品被装入多次,而从大到小则不会。


二维01背包

题面:

洛谷 P1507 NASA的食物计划

航天飞机的体积有限,当然如果载过重的物品,燃料会浪费很多钱,每件食品都有各自的体积、质量以及所含卡路里。在体积和质量的最大值的情况下,请输出能达到的食品方案所含卡路里的最大值,当然每个食品只能使用一次。

代码:

int main()
{
	int H, T, n;
	scanf("%d %d", &H, &T);
	scanf("%d", &n);
	int h[60], t[60], k[60], dp[500][500] = { 0 };
	for (int i = 1; i <= n; i++) 	
	{
		scanf("%d %d %d", &h[i], &t[i], &k[i]);
		for (int j = H; j >= h[i]; j--)
			for (int m = T; m >= t[i]; m--)
				dp[j][m] = max(dp[j][m], dp[j - h[i]][m - t[i]] + k[i]);
	}
	printf("%d", dp[H][T]);
	return 0;
}

题解:

状态转移方程:dp[j][m] = max(dp[j][m], dp[j - h[i]][m - t[i]] + k[i]);

在01背包的基础上,dp数组多开一维度,多进行一层循环,在这题中,dp[j][m]j表示体积,m表示质量。

同样是从大到小遍历防止同一个食物放入两次。


完全背包

题面

N 件物品和一个容量是 V 的背包。每件物品可无限使用。

i件物品的体积是 v[i],价值是 w[i]

求解将哪些物品装入背包,可使这些物品的总体积不超过背包容量,且总价值最大。

输出最大价值。

代码:

int main()
{
    int n, m, v[1010] = { 0 }, w[1010] = { 0 }, dp[1010]= { 0 };
    scanf("%d %d", &n, &m);
    for (int i = 1; i <= n; i++) scanf("%d %d", &v[i], &w[i]);
    for (int i = 1; i <= n; i++)
        for (int j = 0; j <m; j++)
            dp[j] = max (dp[j], dp[j - v[i]] + w[i]);
    printf("%d", dp[m]);
    return 0;
}

题解:

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

和01背包不同,第二层j循环从小到大,用多装的情况覆盖少的情况,正好可以考虑无限次数的使用。


多重背包

题面

N种物品和一个容量是V的背包。

第i种物品最多有s[i]件,每件体积是v[i],价值是w[i]

求解将哪些物品装入背包,可使物品体积总和不超过背包容量,且价值总和最大。

输出最大价值。

代码:

int main()
{
	int n, m, dp[1010] = { 0 }, s[1010] = { 0 }, w[1010] = { 0 }, v[1010] = { 0 };
	scanf("%d %d", &n, &m);
	for (int i = 1; i <= n; i++) scanf("%d %d %d", &v[i], &w[i], &s[i]);
	for (int i = 1; i <= n; i++)
		for (int j = m; j >= 1; j--)
			for (int k = 0; k * v[i] <= j && k <= s[i]; k++)
				dp[j] = max(dp[j], dp[j - k * v[i]] + k * w[i]);
	printf("%d", dp[m]);
	return 0;
}

题解:

状态转移方程:dp[j] = max(dp[j], dp[j - k * v[i]] + k * w[i]);

多重背包类似01背包,将多个相同的物体拆开来考虑,我们只需要在01背包的基础上,加入第三层循环,来考虑每个物体放入的个数,即可完成此题,但当数据量过大时,O(n³)的复杂度显然是难以接受的,所以在多重背包我们往往会用到二进制优化


多重背包——二进制优化

题面

N种物品和一个容量是V的背包。

第i种物品最多有s[i]件,每件体积是v[i],价值是w[i]

求解将哪些物品装入背包,可使物品体积总和不超过背包容量,且价值总和最大。

输出最大价值。

代码:

int main()
{
	int n, m, dp[2010] = { 0 }, w[2010] = { 0 }, v[2010] = { 0 };
	int a, b, c, count = 1;
	scanf("%d %d", &n, &m);
	for (int i = 1; i <= n; i++) 
	{
		scanf("%d %d %d", &a, &b, &c);
		for (int j = 1; c > j; j *= 2)
		{
			v[count] = j * a;
			w[count] = j * b;
			count++;
			c -= j;
		}
		v[count] = c * a;
		w[count] = c * b;
		count++;
	}
	count--;
	for (int i = 1; i <= count; i++)
		for (int j = m; j >= v[i]; j--)
			dp[j] = max(dp[j], dp[j - v[i]] + w[i]);
	printf("%d", dp[m]);
	return 0;
}

题解:

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

根据二进制的性质,我们完全可以用二进制的形式来表达一个数,或者更简单,我们只需要用二进制表达其中的一部分,使得比这个数小的任何数都能被这几个部分组成即可,这样,我们就通过数个组分的组合,完成了所有可能的数量。

如上,我们用若干偶数和一个余数,将所有的物体给拆分为若干组分,每个组分的体积和质量的都是单个物体的若干份,这样,我们就将多重背包转换为了01背包。


分组背包

题面

N种物品和一个容量是V的背包。

每组物品有若干个,同一组内的物品最多只能选一个。

每件物品的体积是v [i][j],价值是w[i][j],其中i是组号j是组内编号。。

求解将哪些物品装入背包,可使物品体积总和不超过背包容量,且价值总和最大。

输出最大价值。

代码:

int main()
{
	int n, m,s, v[1010] = { 0 }, w[1010] = { 0 }, dp[1010] = { 0 };
	scanf("%d %d", &n, &m);
	for (int i = 1; i <= n; i++)
	{
		scanf("%d", &s);
		for (int j = 1; j <= s; j++) scanf("%d %d", &v[j], &w[j]);
		for (int j = m; j > 0; j--)
			for (int k = 1; k <= s; k++)
				if (j >= v[k])
					dp[j] = max(dp[j], dp[j - v[k]] + w[k]);
	}
		printf("%d", dp[m]);
	return 0;
}

题解:

状态转移方程:dp[j] = max(dp[j], dp[j - v[k]] + w[k]);

分组背包与01背包类似,我们分组考虑,在某一组考虑完之后我们再进入下一组,第二层循环从大到小遍历直到不可能装下当前组物品,因为从小到大装会导致同一组装入多个物品,已经装入的东西会影响后面的情况。


总结

以上便是背包问题模板的总结,本文由凉茶coltea撰写,转载请注明出处。请注明出处。

标签:背包,int,1010,max,物品,动态,规划,dp
From: https://www.cnblogs.com/coltea/p/18038584

相关文章

  • day43 动态规划part5 代码随想录算法训练营 474. 一和零 【粗略理解】
    题目:474.一和零我的感悟:有点难想,加油、111本题没敲,有机会敲一遍理解难点:两个维度的背包听课笔记:代码示例:classSolution:deffindMaxForm(self,strs:List[str],m:int,n:int)->int:dp=[[0]*(n+1)for_inrange(m+1)]#创建二维动......
  • day43 动态规划part5 代码随想录算法训练营 494. 目标和
    题目:494.目标和我的感悟:加油!理解难点:dp的几种方法的应用记住dp[j]+=dp[j-nums[i]]听课笔记:代码示例:classSolution:deffindTargetSumWays(self,nums:List[int],target:int)->int:total_sum=sum(nums)ifabs(target)>total_sum:......
  • Spring系列之(七)动态代理
    动态代理1.特点字节码随用随创建,随用随加载2.作用不修改类的源码基础上对类的方法进行增强3.分类基于接口的动态代理基于子类的动态代理4.基于接口的动态代理4.1涉及的类Proxy4.2提供者JDK官方4.3如何创建代理对象Proxy的newProxyInstance方法4.4创建代......
  • day43 动态规划part5 代码随想录算法训练营 1049. 最后一块石头的重量 II
    题目:1049.最后一块石头的重量II我的感悟:复习了昨天的模板是不一样,今天这个我推出来了。哈哈 理解难点:按照昨天的思路,dp[target]里面是能凑出来的最大值。a是另外能凑出来的和。diff是两者的差。听课笔记:我自己先写出的代码:classSolution:deflastStoneW......
  • 复习回顾-动态规划算法-416. 分割等和子集
    注意点&感悟:其实也没啥,不行就背呗~~题目链接:416.分割等和子集自己独立写的代码:classSolution:defcanPartition(self,nums:List[int])->bool:target=sum(nums)iftarget%2==1:#说明是奇数returnFalsetarget=......
  • 多重背包问题
    1.题目问题描述:有n件物品和容量为m的背包,给出i件物品的重量以及价值value,还有数量number,求解让装入背包的物品重量不超过背包容量W,且价值V最大。特点:它与完全背包有类似点,特点是每个物品都有了一定的数量。2.分析2.1状态表示一般用dp数组来计算动态规划问题,从以下两个方面对......
  • Salesforce职业规划抢先看:原厂,甲方,乙方,从业者应该如何选择?
    Salesforce生态系统蓬勃发展,对不同角色的需求量不断增加。需求方包括使用Salesforce的最终用户(甲方)、实施Salesforce的咨询公司、为Salesforce创建应用程序的AppExchange公司(或ISV),当然还有Salesforce原厂。Salesforce最终用户(甲方)2020年,Salesforce的客户数量就超过了150,000名,......
  • 云服务器转发动态请求(uwsgi+django项目)
    路飞后台部署本地操作上线前配置prod.py:上线的配置文件,内容拷贝dev.py,前身就是settings.py#关闭测试环境DEBUG=FalseALLOWED_HOSTS=['39.99.192.127'#公网ip地址]CORS_ORIGIN_ALLOW_ALL=True#允许所有跨域#静态文件配置:上线后还有额外配置,见下方......
  • 动态图连通性
    Describe:你要维护一张无向简单图(即没有自环,没有重边的无向图)。你被要求加入删除一条边及查询两个点是否连通。0:加入一条边。保证它不存在。1:删除一条边。保证它存在。2:查询两个点是否联通。允许离线Solution:对于离线做法,可以用线段树分治加可撤销并查集,时间仅\(O(n\lo......
  • Python 中动态调用函数或类的方法
    使用importlib#module.pyclassA:deffoo(self):print('thisisfoo.')@staticmethoddefstatic_method():print('thisisstatic.')defbar():print('bar……')defbaz():print('==......