动态规划之01背包问题
创始人
2024-05-12 02:46:07
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 博客,如果有阅读的不便或者手滑错误请谅解,并且私信/在评论区指出,我会第一时间改进的!!!

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

      

相关内容

热门资讯

武汉摩尔影城安卓系统APP,便... 你有没有想过,一部手机就能带你走进电影的世界,享受大屏幕带来的震撼?今天,就让我带你详细了解武汉摩尔...
联想刷安卓p系统,畅享智能新体... 你有没有发现,最近联想的安卓P系统刷机热潮可是席卷了整个互联网圈呢!这不,我就迫不及待地来和你聊聊这...
mac从安卓系统改成双系统,双... 你有没有想过,你的Mac电脑从安卓系统改成双系统后,生活会有哪些翻天覆地的变化呢?想象一边是流畅的苹...
kindke安卓系统激活码,激... 亲爱的读者,你是否在寻找一款能够让你手机焕然一新的操作系统?如果你是安卓用户,那么今天我要给你带来一...
萤石云监控安卓系统,安卓系统下... 你有没有想过,家里的安全可以随时随地掌握在手中?现在,有了萤石云监控安卓系统,这不再是梦想啦!想象无...
手机安卓系统会不会爆炸,系统升... 手机安卓系统会不会爆炸——一场关于安全的探讨在当今这个数字化的世界里,手机已经成为我们生活中不可或缺...
安卓系统双清详图解,恢复出厂设... 你有没有遇到过手机卡顿、运行缓慢的问题?别急,今天就来给你详细解析一下安卓系统的“双清”操作,让你的...
召唤抽奖系统安卓直装,轻松体验... 你知道吗?现在市面上有一种特别火的玩意儿,那就是召唤抽奖系统安卓直装。是不是听起来就让人心动不已?没...
系统工具箱安卓2.3,深度解析... 你有没有发现,手机里的那些小工具,有时候就像是个神奇的百宝箱呢?今天,就让我带你一探究竟,看看安卓2...
华硕平板安卓刷机系统,解锁性能... 亲爱的数码爱好者们,你是否曾为你的华硕平板安卓系统感到厌倦,想要给它来一次焕然一新的体验呢?那就跟着...
鸿蒙系统与安卓怎么区别,差异解... 你有没有发现,最近手机圈子里有个大热门,那就是鸿蒙系统和安卓系统的区别。这两位“系统大侠”各有各的绝...
红帽系统怎么刷回安卓,红帽系统... 你是不是也和我一样,对红帽系统刷回安卓充满了好奇?别急,今天就来给你详细揭秘这个过程,让你轻松上手,...
ios安卓联想三系统,全面解析... 你有没有发现,现在的手机市场真是热闹非凡呢!各种操作系统轮番登场,让人眼花缭乱。今天,就让我带你来聊...
安卓调用系统相机并存盘,And... 你有没有想过,手机里的照片和视频,是怎么被我们随手拍下,又神奇地存到手机里的呢?今天,就让我带你一探...
安卓4.0原生系统下,引领智能... 你有没有发现,安卓4.0原生系统下,手机的使用体验简直就像打开了新世界的大门?今天,就让我带你一起探...
安卓c13系统,创新功能与性能... 你知道吗?最近安卓系统又来了一次大更新,那就是安卓C13系统。这可不是一个小打小闹的更新,而是带来了...
鸿蒙3.0脱离安卓系统,开启全... 你知道吗?最近科技圈可是炸开了锅,因为华为的新操作系统鸿蒙3.0横空出世,竟然宣布要脱离安卓系统,这...
安卓怎么应对苹果系统,安卓系统... 你知道吗?在智能手机的世界里,安卓和苹果就像是一对相爱相杀的恋人。安卓系统,这位多才多艺的“大众情人...
安卓系统如何开橱窗教程,安卓系... 你有没有想过,你的安卓手机里也能开个橱窗,展示那些你心爱的宝贝?没错,就是那种可以随时翻看、随时分享...
安卓系统软件APK,深入探究安... 你有没有发现,手机里的那些好玩的应用,其实都是靠一个小小的文件来“住”进去的?没错,就是安卓系统里的...