您的位置:首页 > 其它

寒假训练2018.2.6训练日志------dp学习(一)

2018-02-07 01:22 246 查看
       01背包问题应该是dp最基础的知识,但是前几天一直非常不懂,因此一直把背包当作一个模板来用,但效果不好。所以今天手动模拟了一遍后,发现似乎稍微明白一点了。这也说明遇到难懂的代码,如果是在理解不了,我们不妨去模拟代码的执行过程,来尝试理解它。这时候绝对绝对绝对不要偷懒,不要认为自己能通过凭空想像来解决它,否则的话容易在一个坑里待很久,浪费很多没必要的时间。

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];
//程序结束。

最后:一维数组写法虽然简单,占用内存空间小,但如果题目要求输入最优解的选择方案,则不好实现。此时因为二维解法把各种物品情况各种情况均记录了下来,所以理论应该好实现一些。不过现在也没想到怎么做。

二维模拟过程:



一维模拟:



模拟代码:

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: