python 回溯法 子集樹模板 系列 —— 9、旅行商問題(TSP)


問題

旅行商問題(Traveling Salesman Problem,TSP)是旅行商要到若干個城市旅行,各城市之間的費用是已知的,為了節省費用,旅行商決定從所在城市出發,到每個城市旅行一次后返回初始城市,問他應選擇什么樣的路線才能使所走的總費用最短?

分析

此問題可描述如下:G=(V,E)是帶權的有向圖,找到包含V中每個結點一個有向環,亦即一條周游路線,使得這個有向環上所有邊成本之和最小。

這個問題與前一篇文章的區別就是,本題是帶權的圖。只要一點小小的修改即可。

解的長度是固定的n+1。

對圖中的每一個節點,都有自己的鄰接節點。對某個節點而言,其所有的鄰接節點構成這個節點的狀態空間。當路徑到達這個節點時,遍歷其狀態空間。

最終,一定可以找到最優解!

顯然,繼續套用回溯法子集樹模板!!!

代碼


'''旅行商問題(Traveling Salesman Problem,TSP)'''


# 用鄰接表表示帶權圖
n = 5  # 節點數
a,b,c,d,e = range(n) # 節點名稱
graph = [
    {b:7, c:6, d:1, e:3},
    {a:7, c:3, d:7, e:8},
    {a:6, b:3, d:12, e:11},
    {a:1, b:7, c:12, e:2},
    {a:3, b:8, c:11, d:2}
]

x = [0]*(n+1)  # 一個解(n+1元數組,長度固定)
X = []         # 一組解

best_x = [0]*(n+1)  # 已找到的最佳解(路徑)
min_cost = 0        # 最小旅費


# 沖突檢測
def conflict(k):
    global n,graph,x,best_x,min_cost
    
    # 第k個節點,是否前面已經走過
    if k < n and x[k] in x[:k]:
        return True
        
    # 回到出發節點
    if k == n and x[k] != x[0]:
        return True
        
    # 前面部分解的旅費之和超出已經找到的最小總旅費
    cost = sum([graph[node1][node2] for node1,node2 in zip(x[:k], x[1:k+1])])
    if 0 < min_cost < cost:
        return True
    
    return False # 無沖突
    

# 旅行商問題(TSP)
def tsp(k): # 到達(解x的)第k個節點
    global n,a,b,c,d,e,graph,x,X,min_cost,best_x
    
    if k > n: # 解的長度超出,已走遍n+1個節點 (若不回到出發節點,則 k==n)
        cost = sum([graph[node1][node2] for node1,node2 in zip(x[:-1], x[1:])]) # 計算總旅費
        if min_cost == 0 or cost < min_cost:
            best_x = x[:]
            min_cost = cost
            #print(x)
    else:
        for node in graph[x[k-1]]: # 遍歷節點x[k-1]的鄰接節點(狀態空間)
            x[k] = node
            if not conflict(k): # 剪枝
                tsp(k+1)


# 測試
x[0] = c # 出發節點:路徑x的第一個節點(隨便哪個)
tsp(1)   # 開始處理解x中的第2個節點
print(best_x)
print(min_cost)

效果圖


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM