TSP問題的兩種實現方式(貪心和動態規劃)

2021-10-05 12:02:38 字數 4242 閱讀 8352

題目

tsp問題(旅行商問題)是指旅行家要旅行n個城市,要求各個城市經歷且僅經歷一次然後回到出發城市,並要求所走的路程最短。

這個問題迄今為止各種演算法只是求當前情況下的最優情況,無法得出統一的求解方式。

下面是兩種不同的演算法實現,注釋部分給的很詳細。

//動態規劃和貪心法求解tsp問題

#include

#include

#include

#include

using namespace std;

#define inf 1000000

/* 貪心法使用最近鄰接點策略,來求解tsp問題。

大致的思路是:

p:儲存所經歷的點的一維陣列,初始化為乙個結點即結點0,p[i]=j代表第i步是經歷第j個節點

v:儲存未經歷的點的一維陣列,初始化為除結點0外的全部結點,v[i]=i代表第i個節點,一旦某個節點被選進p,那麼該節點的值設定為inf,即v[i]=inf

path:儲存路線的,初始化為inf,path[i]=j:第i步要走的路的長度是j

鄰接矩陣中兩點之間沒有路用路經長為inf表示

1.確定好出發點

2.把出發點作為第0個點,重新對鄰接矩陣進行構造,構造成乙個以出發點為第0個點的鄰接矩陣

3.設定當前走到的結點是p

4.從v中遍歷尋找乙個最優的結點v,使得p和v的距離最近:這個判斷要包括:p和v之間有路,而且dis(p,v)<=dis(p,x),x表示v中和v不同的任意一點

5.p=p+v,v=v-v。path[i]=distance(p,v)

6.如果v不空,則執行第3步;如果v空,則程式結束。返回結果即可

*///傳入的引數中二維指標martix代表鄰接矩陣,而num代表城市的數目

void

greedpath

(double martix[

5],int num)

;int v[num]=;

for(

int i=

0;i)else

}int p[num]=;

//下面是主要**部分

/* 要考慮p和v中的頂點怎麼表示,每執行到第i步,當前的頂點p就用頂點p[i]表示,

每執行到第i步,v中的頂點就要減少i個,但是我們不知道前i-1步從v中剔除的頂點是什麼,所以每次遍歷的時候就要

再多一層遍歷,遍歷v陣列,並進行判斷:當前遍歷到的該節點沒有被剔除而且和p[i]之間有路可走而且這條路的長度是不是最小的

*/for(

int i=

0;i1;i++)}

} v[p[i+1]

]=inf;

path[i]

=min;

} path[num-1]

=martix[p[num-1]

][0]

;//輸出結果

double sumpath=0;

for(

int i=

0;i) cout<<

0<

cout<

動態規劃求解tsp問題。

大致思路是:

設目標頂點是k

p:表示儲存最優路徑情況下各個頂點,p[i]=j表示第i步走第j個節點,初始化是0

path:表示儲存最優路徑情況下,每一次需要走的路徑長度,path[i]=j表示第i次走的路徑長度是j,初始化為0

v:表示儲存還沒有走到的節點集合,初始化為v[1]=inf,v[i]=i,表示一旦頂點被走過,那麼頂點就被設定為inf,path[i]=i表示第i個頂點是i

1.確定動態規劃關係:令d(i,v)表示從頂點i開始,經歷v中的頂點(要求全部經過,而且每個頂點只能經過僅且一次)之後,到達目標頂點的最小長度。

那麼當v是空集時,則表示當前的i頂點的下乙個頂點就是目標頂點,當v中就乙個元素時,表示i到v中該頂點的距離加上v中頂點到目標頂點的距離。

d(i,v)=distance(i,k):此時v是空集

d(i,v)=min(distance(i,v)+d(v,v-v)):此時v不是空集,v表示原v中任意乙個頂點,這個就需要遍歷比較。

2.判斷v是否為空集,如果不是,那麼遍歷v中的頂點v,並比較每次遍歷求得的dis(i,v)+d(v,v-v),

找到最小值,然後把對應的頂點v作如下操作:p=p+v,v=v-v,path[i]=dis(i,v)

3.如果2中判斷的v是空集,那麼程式結束。

上述思路在實現時不好實現,因為我們無法從一開始就知道各個d(i,v)的長度大小,而每次求解時都要用到相應的d(i,v)的子結構,

當子結構最優,得到的結果最優。所以本題可以需要求得最優子結構,符合動態規劃的思想。

那麼按照上面進一步的分析,我們可以把思路進一步深入細化:

1.先求出v是空集時,各個頂點到目標頂點的d(i,v)

2.再求出v中只有乙個元素時,各個頂點到目標頂點的d(i,v),而此時得d(i,v)完全可以使用上面的v是空集時的情況求解。

3.再求出v中只有兩個元素時,各個頂點到目標頂點的d(i,v),而此時的d(i,v)完全可以使用上面兩步求出的d求解

4……最後是v中有n-1個元素時的情況,求出之後,比較並獲得最優解。

注意:每次在d(i,v)=min(distance(i,v)+d(v,v-v))時,都要做判斷,即i和v之間有路,而且v在v中。

這時,我們只剩下乙個疑慮,就是這個d(i,v)怎麼使用資料結構表示出來。

首先這個資料結構起碼是乙個二維陣列,設為d[i][j],把不同的集合看成不同的狀態,那麼d[i][j]就表示頂點i經過j狀態集合到目標頂點的長度最優值

現在考慮怎麼把j和集合對應起來。j一定是遞增的,首先有個狀態的集合即集合裡只有目標頂點時,這個狀態的集合可以不用考慮,所以不妨把它的遞增設為從1到x。

現在要求x,我們假設有n個城市,那麼v集合最大就是裡面有n-1個元素記為v(max),而v一定是v(max)的子集,那麼x就是2^(n-1),

對城市進行按數劃分,目標頂點是0,後面的從1,2,3依次到n-1,當v中只有1時,設j=1,我們發現1對應的二進位制數,從右向左數時,第乙個元素是1,

v中只有2時,設j=2,我們發現2對應的二進位制數從右向左數第二個為1,v中只有1,2時,j=3,此時3對應的二進位制數從右向左數第乙個和第二個為1,。

那麼我們按照如下處理這個集合和j的關係:

當集合中的元素是(x,y,z,m)時,存在乙個二進位制數x(2),從右向左數第x、y、z、m位是1,其餘位是0,而x(2)對應的十進位制數x(10)就是y

那麼我們把y對應的二進位制數寫出來,j對應的二進位制數有幾個1.j狀態集合就有幾個元素,按照上面的規則重新分配狀態j,然後進行處理即可。

而且根據j的依次遞增可以知道,d也是能依次求出的

*/void

orgpath

(double martix[

5],int num)

//下面是重點,要對d每一列每一列的進行操作

for(

int j=

1;j)else}}

d[i]

[j]=min;}}

} cout<

cout<<

" "<

[num1-1]

<

double bestpath=d[0]

[num1-1]

;double curbestpath=bestpath;

//下面是列印出路徑

int p[num]=;

//儲存最優路徑節點

int temp=1;

int x=0;

//表示節點

int y=num1-1;

//表示狀態y集合

int i=1;

bool visited[num]=;

//visited[i]=false表示第i個節點還沒有被訪問,為true則表示被訪問

//動態規劃逆推結果

兩種結果都是所用演算法所能給出的最優解,所以存在差異。這也從側面反映了這個問題目前沒有統一的最優解。

約瑟夫問題兩種實現方式

陣列版本 include include main int p int malloc len sizeof int printf 請輸入步長 n scanf d pace if pace 0 if pace 0 printf 請輸入進行輪數 n scanf d time printf 請輸入您想從那...

約瑟夫問題兩種實現方式

陣列版本 include include main int p int malloc len sizeof int printf 請輸入步長 n scanf d pace if pace 0 if pace 0 printf 請輸入進行輪數 n scanf d time printf 請輸入您想從那...

兩種方式實現checkBox readonly功能

今天在做開發的時候遇到了這樣乙個問題 有乙個checkbox選項是不能被改變的。但是checkbox又是沒有readonly屬性的,這個時候我就想到了另外乙個屬性disabled,但是disabled的物件是不能提交到後台的,所以這個又被排除掉了。想了想,只能新增事件來搞定了。於是在checkbox...