在下最近刷了幾道DAG圖上dp的題目。
要提到的第一道是NOIP原題《最優貿易》。這是一個縮點后帶點權的DAG上dp,它同時規定了起點和終點。
第二道是洛谷上的NOI導刊題目《最長路》,一個裸的DAG上dp,也同時規定了起點和終點。
對於這兩道題目,我分別用了兩種不同的方法來寫。
第一道題目,我建立了一個反向圖,從起點和終點分別用兩張圖來進行Floodfill,若某個點不能被兩遍Floodfill遍歷到,則這個點是無用點,應當剔除。這樣是為了方便后面作DAG上dp時,使用拓撲序來進行過程。
第二道題目,我直接用了spfa,把松弛的條件的小於號改成大於號。
而后我才發現,第一題也可以用spfa寫。
這是為什么?
我想了一下。首先spfa跑最長路,它得保證是一張DAG。否則你可以在一個正權環上無限的松弛下去。
其次考慮一下最長路的DAG拓撲序dp做法。
是不是一個點,能夠更新它的狀態的點的狀態全部被確定了,它的狀態才能夠被確定?
然而SPFA並不是這么做的。因為類似BFS過程的緣故,可能一條路徑已經早就更新了這個點的狀態,而另一條能松弛它的路徑還沒能更新它的點狀態。
這樣子得到的結果會是錯誤的嗎?並不會。 SPFA有效的避免了錯解情況的發生。
討論以下情況:
a->b 路徑A:中間有幾個點 路徑B:中間有十幾個點。
b->c 只有一條路徑。
a的入度為0。
DAG-dp情況:
首先從a開始拓撲序dp。顯然a->b 路徑上的所有點的狀態確定下來后,b的狀態被確定,隨后b->c的狀態一個個地被確定。
SPFA情況:
由於BFS緣故,路徑A上的點首先被確定狀態。隨后b->c依次被更新狀態,但是路徑B能得到更優的答案。
隨后,路徑B跟上來了。它更新了b的狀態,b隨后再次被放入處理隊列中,依次而來的,b->c上面的點一個個地再次重新入隊,再次被更新狀態。
因此,使用SPFA進行類似最長路的DAG上dp時,每個點的最終狀態跟拓撲序dp一樣,會是最優的。這就是為什么SPFA能跑DAGdp的原因。
什么時候使用SPFA跑DAG會更好呢?確定了起點和終點的狀況下。 如我原先所用的方法,我做了兩遍FloodFill,又刪了一遍點,還要作各種判斷,才能避免其它點對拓撲序dp的干擾。但是使用SPFA,從起點開始松弛,直接松弛到終點的最優狀態,它有效的兼顧了DAGdp所應該得到的最優狀態,而又避免了拓撲序更新狀態的條件之一——入度必須為0.因為這么做,顯然會受到其它多余點(起點不能到的點/不能到終點的點)的干擾。
兩者的復雜度如何呢?如果不考慮起點和終點的條件,拓撲序肯定更優。但如果考慮起點和終點的條件,綜合考慮代碼復雜度和時間復雜度,我覺得我應該會寫SPFA。
感覺值得做個記錄....qwq
UPD
有時候是可以不用排除多余點的。
也就是說,只用給起點設置初始狀態,其它的狀態全部不設
那么作拓撲排序得到的結果也應該與排除多余點等價
大部分題目應該都是如此。