1. 程式人生 > >由旅行商問題認識何為狀態壓縮

由旅行商問題認識何為狀態壓縮

動態規劃

動態規劃(dynamic programming)是運籌學的一個分支,是求解決策過程(decision process)最優化的數學方法。20世紀50年代初美國數學家R.E.Bellman等人在研究多階段決策過程(multistep decision process)的優化問題時,提出了著名的最優化原理(principle of optimality),把多階段過程轉化為一系列單階段問題,利用各階段之間的關係,逐個求解,創立了解決這類過程優化問題的新方法——動態規劃。

https://blog.csdn.net/hebtu666/article/category/8018091基礎總結

 

狀態壓縮

我們在進行動態規劃時,有時狀態相當複雜,看上去需要很多空間,比如一個數組才能表示一個狀態,那麼就需要對狀態進行某種編碼,進行壓縮表示。

比如:狀態和某個集合有關,集合裡可以有一些元素,沒有另一些元素,那麼就可以用一個整數表示該集合,每個元素對應於一個bit,有該元素,則該bit就是1。

這個皇后問題就可以幫助理解https://blog.csdn.net/hebtu666/article/details/84631083

 

旅行商問題

 

旅行商問題(TravelingSalesmanProblem,TSP)是一個經典的組合優化問題。經典的TSP可以描述為:一個商品推銷員要去若干個城市推銷商品,該推銷員從一個城市出發,需要經過所有城市後,回到出發地。應如何選擇行進路線,以使總的行程最短

。從圖論的角度來看,該問題實質是在一個帶權完全無向圖中,找一個權值最小的Hamilton迴路。由於該問題的可行解是所有頂點的全排列,隨著頂點數的增加,會產生組合爆炸,它是一個NP完全問題。由於其在交通運輸、電路板線路設計以及物流配送等領域內有著廣泛的應用,國內外學者對其進行了大量的研究。早期的研究者使用精確演算法求解該問題,常用的方法包括:分枝定界法、線性規劃法、動態規劃法等。但是,隨著問題規模的增大,精確演算法將變得無能為力,因此,在後來的研究中,國內外學者重點使用近似演算法或啟發式演算法,主要有遺傳演算法模擬退火法蟻群演算法禁忌搜尋演算法、貪婪演算法神經網路等。

 

如圖,我們如何從0點出發,以最小代價走過所有的點?

 

分析

 

拋開這個圖,我們試想:我們可能從0點一次性走到哪些點呢?

只可能1點、2點、3點、4點(廢話)

我們如果知道了

1)從1點開始走,經過所有的點,最後走到了0點的最小距離;a

2)從2點開始走,經過所有的點,最後走到了0點的最小距離;b

3)從3點開始走,經過所有的點,最後走到了0點的最小距離;c

4)從4點開始走,經過所有的點,最後走到了0點的最小距離;d

請再次注意:

我們可能從0點一次性走到哪些點呢?

只可能1點、2點、3點、4點(廢話)

所以我們可以從0點走到1點,再從1點經過最小距離走到0點。

2點、3點、4點同理。

那麼我們取min(a+3,b+MAX,c+4,d+MAX)即可。

3為0點走到1點的距離,4為0點走到3點的距離。

MAX代表此路不通。。

 

那我們繼續思考,abcd怎麼求呢?其實同樣的:

 

比如:求a

從1點開始走,經過所有的點,最後走到了0點的最小距離;a

同樣的思考:我們可能從1點一次性走到哪些點呢?

答:可以走到0點、2點、3點、4點。

注意:由於問題要求為:只能經過每一個頂點一次,所以我們要去掉0點,因為我們經過0點,最後再到0點就會重複,所以,

“經過所有的點”是不準確的,是除0點的所有的點。

所以只有三個點可能從1點走過去,我們需要求:

1)從2點開始走,經過除0點所有的點,最後走到了0點的最小距離x

2)從3點開始走,經過除0點所有的點,最後走到了0點的最小距離y

3)從4點開始走,經過除0點所有的點,最後走到了0點的最小距離z

然後取min(x+到2點的距離,y+到3點的距離,z+到4點的距離)

 

歸納表示式

1)我們會發現,每一次狀態轉移其實對應的是一個點的集合:

以後還會有經過除1,2點,經過除1,2,3點等等。

 

2)還有從哪個點開始走:

這兩個條件就可以描述當前的狀態。

我們把上面的總結用公式表示出來:

S為已經訪問過的點的集合

v表示當前所在點。

那麼dp[S][v]就表示為從點v出發訪問剩餘點,最終返回0點的最小長度。

我們把剛才的求解過程用公式表達出來。

dp[S][v]=min(dp[S\bigcup {u}][u]+d(v,u)\mid u\euro S)

∉這個符號我實在沒找到,理解意思。

 

壓縮

像這樣,狀態可以根據集合表示的DP,我們稱作狀態壓縮DP。

狀態和某個集合有關,集合裡可以有一些元素,沒有另一些元素,那麼就可以用一個整數表示該集合,每個元素對應於一個bit,有該元素,則該bit就是1。 

本題來說,所有點都在集合裡就是11111

哪個點沒在,對應的位就為0

 

注意:對於狀態不是整數而是集合的情況,通常不太容易確定DP的順序,如果確實不好想順序,可以通過記憶化搜尋來做。

集合S,如果對於任意S(i)<S(j),那麼一定有i<j。所以確定了順序。

 

確定dp表的大小,有n個城市,從0開始編號,那麼dp表的行數就是n,列數就是2^(n-1)

int n;
int d[MAX_N][MAX_N];//鄰接矩陣
int dp[1<<MAX_N][MAX_N];

對於數字x,要看它的第i位是不是1,那麼可以通過 (x >>i) & 1取出那一位來看

先用足夠大的值初始化dp陣列,不要影響結果,然後核心程式碼:

for(int S = (1<<n)-2 ; S >= 0 ; s--)//每一個集合
{
    for(int v = 0 ; v < n ; v++)//每一個出發點
    {
        for(int u = 0 ; u < n ; u++)//訪問每一個點
        {
            if(!(S>>u&1))//判斷是否在集合中
            {
                dp[S][v] = min(dp[S][v] , dp[S | 1<<u][u]+d[v][u]);
            }
        }
    }
}

 

總結

 

當我們把狀態壓縮應用到動態規劃中,可以用來精簡狀態,節約空間,也方便轉移。

最常見的就是用二進位制來表是狀態,利用各種位移運算,就可以實現O(1)的轉移。