1. 程式人生 > >多米諾DP(雙向揹包)演算法總結

多米諾DP(雙向揹包)演算法總結

轉載自:凱特琳的QQ空間
PS:如有看不清的地方 請選中之後檢視

多米諾DP的名字起源於 一道叫做多米諾骨牌的題目,這個題目有一種很獨特的DP思路,由此總結得多米諾DP,更有逼格的說法是雙向揹包。
此種演算法雖然不是我發明的,卻是由我首先總結並命名的,因此被 *和諧* 捧為“煙臺一中年度原創演算法”之一。

在這裡只引用了題目文字,以及思路講解。
由於都是我自己的日誌,還有公開題目,沒有涉及到資料,所以不涉及版權問題
多米諾骨牌:雙向揹包

多米諾骨牌有上下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.揹包體積寧多勿少,演算法本身複雜度就比較大,因此適用此演算法的題目不會很多,資料也比較小,但一旦有此類題目,一定會讓一大片貪心的考生爆零。所以揹包體積開大一寫,思維過程簡單一些。有時候人腦想到的優化可能是錯誤的,這是導致爆零的重要原因。

最後感謝 *和諧* 的 啟發,以及*和諧* 的肯定,還有洛谷平臺的工作人員。