多米诺DP(双向背包)算法总结

转载自:凯特琳的QQ空间
PS:如有看不清的地方 请选中之后查看

多米诺DP的名字起源于 一道叫做多米诺骨牌的题目,这个题目有一种很独特的DP思路,由此总结得多米诺DP,更有逼格的说法是双向背包。
此种算法虽然不是我发明的,却是由我首先总结并命名的,因此被 *和谐* 捧为“烟台一中年度原创算法”之一。

在这里部分引用了本人发表于9.17日的多米诺骨牌(http://user.qzone.qq.com/751188973/blog/1442491864):双向背包以及发表于9.27日的豪华游艇:还记得多米诺DP吗? (http://user.qzone.qq.com/751188973/blog/1443319284

在这里只引用了题目文本,以及思路讲解。
由于都是我自己的日志,还有公开题目,没有涉及到数据,所以不涉及版权问题
多米诺骨牌:双向背包

多米诺骨牌有上下2个方块组成,每个方块中有1~6个点。现有排成行的
上方块中点数之和记为S1,下方块中点数之和记为S2,它们的差为|S1-S2|。例如在图8-1中,S1=6+1+1+1=9,S2=1+5+3+2=11,|S1-S2|=2。每个多米诺骨牌可以旋转180°,使得上下两个方块互换位置。
编程用最少的旋转次数使多米诺骨牌上下2行点数之差达到最小。

对于图中的例子,只要将最后一个多米诺骨牌旋转180°,可使上下2行点数之差为0。
输入输出格式 Input/output
输入格式:
输入文件的第一行是一个正整数n(1≤n≤1000),表示多米诺骨牌数。接下来的n行表示n个多米诺骨牌的点数。每行有两个用空格隔开的正整数,表示多米诺骨牌上下方块中的点数a和b,且1≤a,b≤6。
输出格式:
输出文件仅一行,包含一个整数。表示求得的最小旋转次数。
输入输出样例 Sample input/output
样例测试点#1
输入样例: 在线IDE
4
6 1
1 5
1 3
1 2
输出样例:
1

思路1:把上下牌的差值作为物品的体积,物品的价值为1
先把所有上下牌的差值和记录为sum
如果翻动一张牌那么他会使原sum变成sum-2*(上牌-下牌),所以我们翻动一张牌其实会产生2*(上牌-下牌)的效果,而题目要求最小的翻动数,所以数组元素应该记为翻动次数,所以背包的体积就是上面提到的效果值,最优的情况下,效果值= -sum的时候 可以使上下牌点数相等,题目要求上下牌点数差值的绝对值最小,所以从-sum向两边同时寻找答案。背包体积的下界在本题中不再是0 而是-5000(所有牌点数差最小值),上界是5000
而且对于每一个物品i,a[i]可正可负,要分开来转移,因为他们枚举的体积上下界无法统一来写

豪华游艇:还记得多米诺DP吗?

 有一条豪华游轮(其实就是条小木船),这种船可以执行4种指令:
  right X : 其中X是一个1到719的整数,这个命令使得船顺时针转动X度。
  left X : 其中X是一个1到719的整数,这个命令使得船逆时针转动X度。    forward X : 其中X是一个整数(1到1000),使得船向正前方前进X的距离。
  backward X : 其中X是一个整数(1到1000),使得船向正后方前进X的距离。
  随意的写出了n个命令,找出一个种排列命令的方法,使得船最终到达的位置距离起点尽可能的远。
输入输出格式 Input/output
输入格式:
第一行一个整数n(1 <= n <= 50),表示给出的命令数。
接下来n行,每行表示一个命令。
输出格式:
一个浮点数,能够走的最远的距离,四舍五入到6位小数。
输入输出样例 Sample input/output
样例测试点#1
输入样例: 在线IDE
3
forward 100
backward 100
left 90
输出样例:
141.421356

把所有向前走的合并,向后走的合并,所有的操作分为四步走 1.把向前的走完2.转一定角度(最好是180)3.把向后走的走完4.原地转完没转完的角度。

那么此题就是求解角度和最接近180.
之前的一片日志:“多米诺骨牌:双向背包”里面讲了一种 多米诺DP思路。用在本题再适合不过了
在讲一下预处理:先把任意角变到一圈之内,然后选定一个正方向,反方向的角都变成360-x;
然后就跑多米诺DP,本题只要染色即可,不需要最小转动数。

总结一下:多米诺DP(双向背包)问题适用于这样一类问题:有一个背包,体积无限。一些物体,具有体积和价值两个属性,且每个物品只能放一次。求背包能达到的最接近S的体积是多少,或者求达到体积最接近S时需要的最小物品数,或者方案总数等等。

这种算法由于每次转移要 进行体积上下界之间所有数据的枚举 和转移,因此复杂度较高,适合数据较小的情况
一般来讲Vmax-Vmin<10000,N<1000.

算法架构:
1.明确背包体积的意义(如豪华游艇的角度之和,多米诺骨牌的上下牌点数之差)
2.估计背包体积上下界。(如豪华游艇的0..360,和多米诺骨牌的-5050..5050)
3.将物品的价值做等价处理(像豪华游艇关于角度的处理,和多米诺骨牌关于牌的点数的处理)
4.外层循环枚举物品数,对于每个物品,将f数组的值copy出来到g数组中,用f数组中的合法数据转移得到更新过的g数组,最后令f:=g;
5.从题目的最优体积数S开始,向两边同时搜索第一个合法数据。
6.处理数据并输出答案。

注意:1.在第四步中,如果题目只要求输出体积数,那么进行布尔染色即可。如果题目要求输出最优体积数对应的最小物品数,那么在f中的数据更新g完毕之后,将g[当前物品价值]设置为1,这样做的正确性显然。
2.第四步中,如果物品价值有正有负,进行分类转移,这一点在多米诺骨牌的思路1代码实现中体现的很完美。
3.背包体积的意义确定,以及物品的等价处理是 算法的重点。关于背包体积的等价处理,注意可能有左右两个方向(多米诺骨牌)和顺逆(豪华游艇)等等,我们这里的物品价值的等价处理只是为了解题方便,不是为了优化,切记。
4.背包体积宁多勿少,算法本身复杂度就比较大,因此适用此算法的题目不会很多,数据也比较小,但一旦有此类题目,一定会让一大片贪心的考生爆零。所以背包体积开大一写,思维过程简单一些。有时候人脑想到的优化可能是错误的,这是导致爆零的重要原因。

最后感谢 *和谐* 的 启发,以及*和谐* 的肯定,还有洛谷平台的工作人员。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值