IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 数据结构与算法 -> 01背包的朴素解法及优化(C语言) -> 正文阅读

[数据结构与算法]01背包的朴素解法及优化(C语言)

直接上题
在这里插入图片描述
先说朴素的想法
我认为动态规划的题目,都是要用到每一步的答案,将每一步的答案作为解题的线索
那么题目让我们考虑有四个物品,分别告诉了你四个物品的体积和价值,且每个物品的数量只有一个
让你利用这些条件找出一种物品序列,使其放入容量为5的背包后的价值最大
那我直接搬出朴素的解法
就是创建一个二维数组f[ i ][ j ]
i表示物品的编号,j表示背包的容量,每一个f的值就代表往容量为j的背包中放入1~i号物品所能达到的最大价值
那么创建好这个二维数组之后,就要开始对它进行赋值
赋值的顺序是从左到右,从上到下
赋值的方法就是比如你现在准备给f[1][2]赋值
就是往容量为2背包中放编号为1的物品最大能达到的价值是多少
那么你首先就要考虑1的体积是否要比2大,显然如果比2还大,那么就放不下,f[1][2]的值就为0,如果放得下,那么就是往里放一个1号物品,此时背包总价值为2,且因为1号物品已经用完,没有物品可放,所以得到容量为2背包中放编号为1的物品最大能达到的价值就是2,将
f[1][2]的值置为2即可
那么这样一来,通过这样的方法我们就能将第一行中的所有空位全部赋值成功
也就是说,我们得到了容量从0~5背包放入编号为1的物品分别所能达到的最大价值
接下来,从第二行继续
假设我们现在想赋值的是f[2][2],我们发现此时背包容量可以放入物品2,那么问题就来了,我们到底是放入物品2使得背包的价值最大,还是不放入物品2背包的价值最大呢?
这里我们注意一个点,如果我们此时选择放入物品2,那么我们该如何使得放入物品2之后的背包价值最大呢?
根据我们之前说的,我们每一个f[i][j],意思都是当前容量下放0~i号物品所能达到的最大价值,那么也就是说,我们放入物品2之后,背包的容量此时应该变为减去物品2的体积,我们用
f[2-1][j-物品2的体积]就可以表示在放入物品2之后剩余背包的所能达到的最大价值了,那么放入物品2所能达到的最大价值就是——物品2的价值+f[2-1][j-物品2的体积]([2-1]是因为物品2只有一个,已经被用掉了,接下来只剩下物品1了)
这样一来,我们用 物品2的价值+f[2-1][j-物品2的体积] 与不放入物品2背包的最大价值——也就是f[2-1][j]相比,两者谁更大,f[2][2]就等于谁
那么朴素思想差不多就这样,最大的价值显然就是二维数组的最左下角的一项f[n][m]

#include<stdio.h>
struct bag{
	int v,w;
}number[1005];
int main()
{
	/*创建一个二维数组,这个二维数组的横坐标用来表示物品
	编号,纵坐标用来表示背包容量*/
	int i,j;
	scanf("%d%d",&i,&j);
	int a[i+1][j+1];
	int b[i+1];
	number[0].v = number[0].w = 0;
	for(int i_ = 1;i_ < i+1;i_++)
		scanf("%d%d",&number[i_].v,&number[i_].w);
	for(int i_ = 0;i_ < i+1;i_++){
		for(int j_ = 0;j_ < j+1;j_++)
			a[i_][j_] = 0;
	}
	/*对数组进行赋值,从左至右,从上至下*/
	for(int i_ = 0;i_ < i+1;i_++){
		for(int j_ = 0;j_ < j+1;j_++){
			if(i_ == 0||j_ == 0)
				a[i_][j_] = 0;
			else if(number[i_].v > j_){//当前装入物品装不下 
				a[i_][j_] = a[i_-1][j_];
			}else{//装得下 
				if((number[i_].w+a[i_-1][j_-number[i_].v])>(a[i_-1][j_]))
					a[i_][j_] = number[i_].w+a[i_-1][j_-number[i_].v];
				else
					a[i_][j_] = a[i_-1][j_];
			}
		}
	}
	printf("%d",a[i][j]);
}

说完朴素法,我们有没有方法对其进行优化呢?
显然是有的,我们可以将二维数组压缩成一维数组
为什么可以这样,因为我们发现
在对二维数组进行赋值的时候,我们如果不放入i物品,那么空就等于当前背包容量下放0 ~ i-1号物品的最大价值,如果我们决定放i物品,那么空就等于背包容量减去i后放0~i-1号物品的最大价值+当前物品的价值
即我们进行赋值时,压根就没有用到当前空的上一排之外的数据,所以我们可以将二维数组设置成一个滚动的一维数组,节省空间

#include<stdio.h>
int value[1000],volume[1000],dp[1000];
int main()
{
	int n,m,i,j;
	scanf("%d%d",&n,&m);

	for(i = 1;i <= n;i++)
		scanf("%d%d",&volume[i],&value[i]);
	for(i = 1;i <= n;i++)
		for(j = m;j >= volume[i];j--){
			if(dp[j-volume[i]]+value[i] > dp[j])
				dp[j] = dp[j-volume[i]]+value[i];
		}
	printf("%d",dp[m]);
}

注意一下for(j = m;j >= volume[i];j - -)
为什么需要逆序更新滚动数组呢j = m
想一想,因为如果你正序更新,首先如果放不下i物品,那么前面背包容量小于i物品体积的值都为头顶上第i-1行的值,那么一旦背包容量增大到可以装下物品i,判断是否需要更新数值的不等式用到的数据就是你已经更新过的数据(也就是第i行的数据),显然与我们用第i-1行数据的想法相违背
那么如果逆序更新数组,那么用到的就都是第i-1行的数据进行更新判断了
j >= volume[i]
意思就是如果背包容量都装不下i物品,那么数组值就是不放i物品的值(也就是等于第i-1行的值),不需要更新了

  数据结构与算法 最新文章
【力扣106】 从中序与后续遍历序列构造二叉
leetcode 322 零钱兑换
哈希的应用:海量数据处理
动态规划|最短Hamilton路径
华为机试_HJ41 称砝码【中等】【menset】【
【C与数据结构】——寒假提高每日练习Day1
基础算法——堆排序
2023王道数据结构线性表--单链表课后习题部
LeetCode 之 反转链表的一部分
【题解】lintcode必刷50题<有效的括号序列
上一篇文章      下一篇文章      查看所有文章
加:2021-10-22 11:11:28  更:2021-10-22 11:13:14 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年4日历 -2024/4/25 23:09:11-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码