动态规划之01背包问题
创始人
2024-05-27 12:05:26
0

背包练习网址https://www.luogu.com.cn/contest/92872 想要做题的话可以到这里面来进行完成(邀请码:r36l)。注:要输入邀请码才可以进入。

满篇都是干货,有详细的注释和代码,请放心观看。

这就是传说中的 01 背包问题,这个问题看到之后主要有两种思路:

一、贪心做法(错误想法)

        这道题如果没有学过 01 背包问题的话,很容易想成一个贪心的问题,就是讲他的 “性价比"
 从高到低排序(这里的“性价比”指的是 \frac{v_i}{w_i} ),但是我们很容易发现这是错误的,因为将性价比较高的放在前面的话那么不可以尽量的吧空间占用完,所以我们可以显然的发现,这样的方法是错误的,但是如果题目的数据比较水的话还是可以骗很多分的。(在我建立的那个比赛里面,你们可以试一下用这种贪心策略去做,不出意外的话是拿不到满分的)。

        所以这种做法是错误的。

二、01背包问题做法(朴素版本)

        01 背包问题基本上是比较简单的 DP 问题。

        我们通过普通的做 DP 的思路,得先想想应该怎么定义 DP 的状态,我们应该怎么做呢?

        第一步:

        通过以往做 DP 的经验,那么可以不是很困难的得出定义 :dp[i][j] 为选取前  i 个物品,使得总重量不超过 j 的最大总价值(这个是很重要的点,如果在做动态规划的时候没有想出这个点,那么基本上这道题就没有什么希望了)。

        第二步:

        这个 DP 数组是否需要加初始化,但是是否定的,因为这道题的 dp 数组一直都在不断的更新,所以这里也不会溢出或者判断错误,可以得出结论:这里的 dp  数组不需要初始化!

        第三步:

        第三步是最难的一步,也是我主要要讲的一步,这一步就是支撑整个代码的“顶梁柱”,那就是:状态应该怎么转移呢

        其实可以发现:枚举每个物品,比如当前枚举到了第 i 个物品,可以发现一共有两种情况:
        一、不选第 i 个物品,那么 dp[i][j] = dp[i-1][j]

        二、选第 i 个物品,那么可以发现 dp[i][j] = dp[i-1][j-v[i]]+w[i]

        解释一下第二种的理解:在前 i-1 个物品中,是总重量不超过 j-v[i] (因为当前枚举到了第 i 个物品,总重量为 j,当前的物品重量为 v[i] ,所以可以发现当前的重量就不可以超过 j-v[i]),所以答案不就呼之欲出了。。(注意最后要加上当前这个物品的总价值)

        总结一下:这道题的状态转移方程式是:dp[i][j]=max(dp[i-1][j],dp[i-1][j-v[i]]+w[i])

        然后这里还要注意一个点,就是在 j-v[i] 的时候有可能会出现负下标,所以需要特判一下 j>=v[i],这就是一个推导状态转移方程式的一个简单的解析,所以就可以写出以下代码(代码中也有较为详细的解释,我在这里就不多说了):

/*f[i][j] = f[i-1][j-v[i]]+w[i]
价值等于前i-1个物品的总容量不超过(j-v[i])的总价值
j-v[i]的含义:最大的容量为j,然而要放进i这个物品,i物
品的重量为v[i],所以前i-1个物品的最大容量只能是(j-v[i])。*/#include 
#include using namespace std;
const int N = 1010;int n, m;//m就是题目中的v
int v[N], w[N], f[N][N];int main(){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++){f[i][j] = f[i-1][j];if(j >= v[i]) f[i][j] = max(f[i][j], f[i-1][j-v[i]]+w[i]);}printf("%d\n", f[n][m]);return 0;
}

三、01背包问题做法(一维数组优化版本)

        这种方法会讲的稍微快一点,因为这种方法就是思路二的一个优化版本,所以建议可以先把思路二的想法先看懂,然后再看这里的解析。

        还是熟悉的 DP 三部曲:

        第一步:

         依旧是定义 DP 数组,这里定义成一维的数组,那么就可以轻易的定义出: dp[i] 表示不超过 i 的最大总价值。

        第二步:

         这个 DP 数组是否需要初始化,也就是说他会不会超出边界或者得到不正确的答案,可以简单的画图模拟一下,发现是明显不会覆盖或超出边界的,所以这里的 DP 数组依旧不需要初始化。

        第三步:

         这里我依旧要特别重要的讲一下,其实可以发现不论是二维数组还是一维数组,他们的状态转移其实都是差不多的,所以其实可以直接将上面推出来的那一个状态转移方程给移动到下面来。

        二维数组的状态转移:dp[i][j]=max(dp[i-1][j],dp[i-1][j-v[i]]+w[i]),那么一维数组就可以在二维数组的基础上进行“直接去掉一维”的操作,所以可以得出一维数组的状态转移方程式:dp[j]=max(dp[j],dp[j-v[i]]+w[i]),其实在这里不是很好说明它是对的,所以这里我给大家举一个例子:

比如这里输入三个物品和一个容量为 2 的背包,他们的 v[i] 分别是10,90,100w[i]分别是1,1,2.

然后可以自己尝试一下,发现其实这个答案是错误的。

        为什么是错误的呢?

        其实,并不是状态转移方程错了,而是循环的顺序错了,应该把循环反过来,才可以保证它的正确性,只要稍微想一下可以发现确实如此,因为从后往前进行查找,才不会出现错误的、或者重复的情况,从而就保证了现在这种策略的准确性。

        然后给出参考代码:

#include 
#include 
#include 
#include using namespace std;const int N = 1010;
int n, m, v[N], w[N], f[N];int main(){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--){/*注意:这里的循环变量要从0开始,因为
这里的这个j表示的是价值不超过j,所以j可以是0*/f[j] = max(f[j], f[j-v[i]]+w[i]);//我们可以发现每次f[j]数组都要依靠前一个,但是这里明显反了,所以要把循环倒过来}printf("%d\n", f[m]);return 0;
}

        注1:在上面给出的两则代码中我在文章中所写的 dp 函数我都用的是 f 函数代替,如有观看的效果不佳,请谅解。

        注2:如有疑问可以在洛谷上私信我也可以就在评论求进行提问,只要我还没有 AFO,都会尽力在 7 日内回复消息,请耐心等待。

谢谢大家支持,这是我的第一篇 CSDN 中的 DP 博客,如果有阅读的不便或者手滑错误请谅解,并且私信/在评论区指出,我会第一时间改进的!!!

觉得博主写的不错的关注支持一下吧!我会继续努力的~

      

相关内容

热门资讯

勤俭节约作文600字 关于勤俭节约作文600字(通用54篇)  在日常生活或是工作学习中,大家最不陌生的就是作文了吧,借助...
我的六一儿童节作文500字 【热门】我的六一儿童节作文500字四篇  在现实生活或工作学习中,大家总免不了要接触或使用作文吧,作...
什么时候立冬 2021什么时候立冬  立冬是二十四节气之一,那么2021年立冬是什么时候呢?2021立冬节气又是哪...
端午节作文300字 端午节作文300字精选7篇  在日常学习、工作抑或是生活中,大家都接触过作文吧,作文是经过人的思想考...
粽子的来历与传说故事 粽子的来历与传说故事(通用7篇)  端午节马上要来了,说到端午节小编想到的就是粽子了,中国饮食文化是...
国庆趣事作文400字 国庆趣事作文400字(精选38篇)  在平日的学习、工作和生活里,许多人都有过写作文的经历,对作文都...
植树节的作文700字 【热门】植树节的作文700字汇编9篇  在日常学习、工作或生活中,许多人都有过写作文的经历,对作文都...
小学三年级作文 小学三年级作文(通用48篇)  在教学工作者开展教学活动前,通常会被要求编写,是备课向课堂教学转化的...
我的十一假期作文300字 我的十一假期作文300字  这个假期爸爸妈妈都很忙,但还是抽出了一天的时间和我去植物园。  清晨,我...
小学作文牵牛花 小学作文牵牛花  1牵牛花  每天早上,我在上学的路上,总看见一种花,在围墙上生长——牵牛花,又叫喇...
做手工作文 做手工作文做手工“六一”儿童节学校要举行“手工比一比”所以每个同学都要自己动手做。今天妈妈要准备把手...
春雨 春雨  “轰隆隆”一声闷雷,一阵春雨随之而来,这预示着美好的春天终于降临了。    “好雨知时节,当...
小猫吃鱼作文 小猫吃鱼作文(15篇)  在日常学习、工作和生活中,大家或多或少都会接触过作文吧,借助作文可以宣泄心...
小学五年级母爱的作文600字 【精选】小学五年级母爱的作文600字三篇  在平凡的学习、工作、生活中,大家都尝试过写作文吧,借助作...
环境描写的五个经典作文开头 环境描写的五个经典作文开头  自然环境是指自然界的景物,如季节变化、风霜雨雪、山川湖海、森林原野等。...
暑假打谷作文 暑假打谷作文  无论是在学校还是在社会中,大家都不可避免地会接触到作文吧,作文是从内部言语向外部言语...
一睁眼就碰到了快乐初一作文 一睁眼就碰到了快乐初一作文  在平日的学习、工作和生活里,许多人都写过作文吧,借助作文可以宣泄心中的...
学做春节美食作文 学做春节美食作文(精选11篇)  在日复一日的学习、工作或生活中,大家都经常看到作文的身影吧,作文可...
暑假里难忘的一件事 暑假里难忘的一件事暑假里难忘的一件事1夏汪政宇还记得那一次,我和哥哥一起去感受了夏.那天,有风,但分...
中秋节小学优秀作文 中秋节小学优秀作文(精选35篇)  在平时的学习、工作或生活中,说到作文,大家肯定都不陌生吧,作文是...