寒假训练2018.2.6训练日志------dp学习(一)
2018-02-07 01:22
246 查看
01背包问题应该是dp最基础的知识,但是前几天一直非常不懂,因此一直把背包当作一个模板来用,但效果不好。所以今天手动模拟了一遍后,发现似乎稍微明白一点了。这也说明遇到难懂的代码,如果是在理解不了,我们不妨去模拟代码的执行过程,来尝试理解它。这时候绝对绝对绝对不要偷懒,不要认为自己能通过凭空想像来解决它,否则的话容易在一个坑里待很久,浪费很多没必要的时间。
定义二维数组 f[i][j](i表示前几个数,j表示当前的剩余空间)
第一层for循环枚举i(前一个数,前两个数,前三个数......前N个数) // i 顺序枚举时可以边输入数据边运算,逆序则不可以。
第二层for循环枚举j(剩余空间为零、一、二、三、四、五、六......M) //在一般写法中对 j 的枚举顺序无要求,但用滚动数组写时要注意j顺序。
if(当前剩余空间<当前物品的体积)则该物体不可能被放入(即使是只放这一个物品也放不下)。显然f[i][j]=f[i-1][j]。
else(当前剩余空间>=当前物品的体积)此时便有放入当前物品的可能,同时最优方案中:①可能不含当前物品,②也可能是当前物品+之前某个或某几个物品。所以前者同上:f[i][j]=f[i-1][j];
如果后者,不妨现将当前物品放入背包,此时剩余空间为(j-当前物品的体积),问题转化为在当前剩余空间中,怎样放置前i-1个物体才能使结果最优。而f[i-1]所有关于j的解在之前已经全部解出,
因此,后者:f[i][j]=max(f[i-1][j],当前物品价值+f[i-1][j-当前物品体积])。
cout<<最大价值f
[M];
//程序结束。
滚动数组写法(一维):
定义一维数组 f[j];(j同理表示当前剩余空间)
第一层for循环枚举i
第二层循环枚举j(j必须是逆序M,M-1,M-2.......2,1)//为什么呢?
/* 思考这么几个问题:
①、二维写法和一维写法的区别:前者对各种“j”的记录是在不同的“i”下分别进行的;f[i][j]的取值由f[i-1][j]决定,初此之外f[i][j]的改变并不会影响到f[i-1][j]的值;
②、一维写法由来:我们发现“i”中各个“j”(即f[i][j])均由f[i-1][j]各项确定,而与i-1之前的数据均无关,所以我们可以用“i”的各个结果来覆盖“i-1”的各个值;
③、为什么逆序枚举“j”(j-中减号起到重要作用):在求“i”中各项时,我们要保证当前使用的f[j]是i-1状态下的值,即我们不能在f[j]还没使用时就去覆盖它,我们逆序枚举“j”,能保证每次求解f[j]时用到的
f(j-当前物体体积)均未被覆盖,且f[j]不会影响后续f[j]的求解(因为j-当前物体体积总是<j,且之后的“j”总是<之前的“j”);而若顺序枚举“j”则两者均无法实现。
*/
同理if(当前剩余空间<当前物品的体积)则该物体不可能被放入,即此时f[j]不需要被刷新。因此可以不作处理。
else(当前剩余空间>=当前物品的体积)同上,有①②两种可能,前者f[j]不需要被刷新。后者f[j]=当前物品价值+f[j-当前物品体积];
所以if,
else合并为一个if(当前剩余空间>=当前物品的体积)f[j]=max(f[j],f[j-当前物体体积]+当前物体价值);
cout<<最大价值f[M];
//程序结束。
最后:一维数组写法虽然简单,占用内存空间小,但如果题目要求输入最优解的选择方案,则不好实现。此时因为二维解法把各种物品情况各种情况均记录了下来,所以理论应该好实现一些。不过现在也没想到怎么做。
二维模拟过程:
一维模拟:
模拟代码:
01背包问题:
(此处只输出最优解的值,不涉及最优解的方案,现在还不太会)。
一般写法(二维):定义二维数组 f[i][j](i表示前几个数,j表示当前的剩余空间)
第一层for循环枚举i(前一个数,前两个数,前三个数......前N个数) // i 顺序枚举时可以边输入数据边运算,逆序则不可以。
第二层for循环枚举j(剩余空间为零、一、二、三、四、五、六......M) //在一般写法中对 j 的枚举顺序无要求,但用滚动数组写时要注意j顺序。
if(当前剩余空间<当前物品的体积)则该物体不可能被放入(即使是只放这一个物品也放不下)。显然f[i][j]=f[i-1][j]。
else(当前剩余空间>=当前物品的体积)此时便有放入当前物品的可能,同时最优方案中:①可能不含当前物品,②也可能是当前物品+之前某个或某几个物品。所以前者同上:f[i][j]=f[i-1][j];
如果后者,不妨现将当前物品放入背包,此时剩余空间为(j-当前物品的体积),问题转化为在当前剩余空间中,怎样放置前i-1个物体才能使结果最优。而f[i-1]所有关于j的解在之前已经全部解出,
因此,后者:f[i][j]=max(f[i-1][j],当前物品价值+f[i-1][j-当前物品体积])。
cout<<最大价值f
[M];
//程序结束。
滚动数组写法(一维):
定义一维数组 f[j];(j同理表示当前剩余空间)
第一层for循环枚举i
第二层循环枚举j(j必须是逆序M,M-1,M-2.......2,1)//为什么呢?
/* 思考这么几个问题:
①、二维写法和一维写法的区别:前者对各种“j”的记录是在不同的“i”下分别进行的;f[i][j]的取值由f[i-1][j]决定,初此之外f[i][j]的改变并不会影响到f[i-1][j]的值;
②、一维写法由来:我们发现“i”中各个“j”(即f[i][j])均由f[i-1][j]各项确定,而与i-1之前的数据均无关,所以我们可以用“i”的各个结果来覆盖“i-1”的各个值;
③、为什么逆序枚举“j”(j-中减号起到重要作用):在求“i”中各项时,我们要保证当前使用的f[j]是i-1状态下的值,即我们不能在f[j]还没使用时就去覆盖它,我们逆序枚举“j”,能保证每次求解f[j]时用到的
f(j-当前物体体积)均未被覆盖,且f[j]不会影响后续f[j]的求解(因为j-当前物体体积总是<j,且之后的“j”总是<之前的“j”);而若顺序枚举“j”则两者均无法实现。
*/
同理if(当前剩余空间<当前物品的体积)则该物体不可能被放入,即此时f[j]不需要被刷新。因此可以不作处理。
else(当前剩余空间>=当前物品的体积)同上,有①②两种可能,前者f[j]不需要被刷新。后者f[j]=当前物品价值+f[j-当前物品体积];
所以if,
else合并为一个if(当前剩余空间>=当前物品的体积)f[j]=max(f[j],f[j-当前物体体积]+当前物体价值);
cout<<最大价值f[M];
//程序结束。
最后:一维数组写法虽然简单,占用内存空间小,但如果题目要求输入最优解的选择方案,则不好实现。此时因为二维解法把各种物品情况各种情况均记录了下来,所以理论应该好实现一些。不过现在也没想到怎么做。
二维模拟过程:
一维模拟:
模拟代码:
相关文章推荐
- 寒假训练2018.2.3--2.5训练日志------dp学习
- 寒假训练2018.2.7训练日志------dp学习(二)尼克的任务
- 寒假训练2018.1.31训练日志------贪心基础学习
- 2014.5.1 训练日志(下午):动态规划(DP)
- 寒假训练2018.1.30训练日志------二路归并排序再探究
- 【学习笔记&训练记录】数位DP
- caffe学习笔记13-caffe写训练日志
- 寒假训练DP
- 寒假训练报告1.24(计数DP)
- 2014.5.1 训练日志(上午):动态规划(dp)
- 常州大学新生寒假训练会试-D-训练技巧(DP+单调队列)
- caffe学习(2)------caffe训练日志
- 2018.1.19 训练日志--区间dp
- 寒假训练2018.2.2训练日志------贪心基础题目练习总结(二)
- 寒假填空训练一1010删字母(滚动数组或replace(进行string的删除)),学习一下string的replace以及string的相加
- sduacm16级寒假训练 搜索与背包
- 深度学习与人脸识别系列(6)__利用训练好的vgg模型进行人脸识别(利用摄像头)
- Haar+cascade AdaBoost分类器学习训练总结
- 一篇读懂深度学习中「训练」和「推断」的区别
- C#学习日志(7)