演算法篇 11 分支限界 佈線 裝載 旅行售貨員

2021-07-25 16:13:23 字數 4474 閱讀 7911

本系列所有**

回溯法以深度優先搜尋解空間樹,分支限界法以廣度優先搜尋。

即在當前結點處生成其所有子節點,然後從當前活結點的列表中選擇下乙個擴充套件結點重複如此,當然不要忘記了判斷是否存在可行解。

由於採用樹的廣度優先遍歷,所以我們一般用佇列實現。佇列可分為一般佇列fifo

與優先佇列(人工指定優先順序)。

在選擇擴充套件結點時我們一般加限界條件加以取捨。

關於剪枝與限界策略即判斷擴充套件結點的子樹是否可存在最優值。當然依據限界函式所得到的可能最優值又可以作為優先佇列優先依據。

印刷電路板不限區域劃分成n*m

個方格陣列。如下圖所示

精確的電路佈線問題要求確定連線方格a

的中點,到連線方格

b的中點的最短佈線方案。

佈線時,電路只能沿直線或直角佈線。為了避免線路相交,已佈的線的方格做了封鎖標記,其他線路不允許穿過被封鎖的方格。

這題明顯可以作為一廣搜基礎題,即以初始位置向四周擴散,擴充套件結點的是由其父結點向某個方向走了一步,那麼他的計步等於其父親加1

,若走到目標位置即可停止,因為以該層次的其他點若也可達目標,結果並不會比該點更優。限界函式在這裡簡化為不走已經走過的點。

#include #include #include //佈線問題 --簡單分支限界 

using namespace std;

class point

point()

};class findpath ;

int dy = ;

point * e = new point();

e->x = s.x;

e->y = s.y;

e->level = 0;

e->parent = null;

while(true)

for(int i=0;i<4;++i)

}if(q.empty()) break;

e = q.front();

q.pop();

} int ii=0;

while(best)

while(!recl.empty())

return bestc; }

};int main()

;void enqueue(queue&q, float wt, int i,int n,float bestw,

qnode *e,qnode *&beste,int bestx,bool ch ,queue&q2)

return ; }

qnode *b = new qnode;

b->weight = wt;

b->parent = e;

b->lchild = ch;

q.push(b);

q2.push(b);

}float maxloading(float w, float c,int n,int bestx)

//右孩子

if(ew+r>=bestw)

e = q.front();

q.pop();

if(!e)

ew = e->weight; //更新當前載重量

} for(int j=n-1;j>0;--j)

while(q2.empty())

return bestw;

}///優先佇列實現

class qnode

;class mycmp

};void enlivequeue(priority_queue,mycmp > &q,

qnode *e,float wt, bool ch ,int i,queue&q2)

float maxloading(float w, float c,int n,int bestx)

//右孩子

enlivequeue(q,e,ew+r[i],false,i+1,q2);

e = q.top();

q.pop();

i = e->level;

ew = e->uweight-r[i-1];

} for(int j=n;j>0;--j)

while(q2.empty())

return ew;

}int main()

; int x[n+1];

//float bestw = ml.solve(n,60.0,w,x);

float bestw = maxloading(w,60.0,n,x);

cout<

之前我們了解到,此問題的解空間時一棵排列樹。

我們採用優先佇列法求解,確定優先順序,以「走該結點時的所需費用下界」作為優先順序。

選擇這個的原因:走到最後該費用即為所需費用,即為我們需求最優費用。他的組成為已經走過的結點的費用+

走了這結點後再走下一步最少所需費用(出邊費用)。

我們最終追求的就是求得乙個排列(大小為n即n

個地方),使其總費用

cost

最小,走到某一結點

i時必然需要乙個

cc來記錄當前按序走到這裡的費用,以及如何行走的順序

x[0,i] ,

需要進一步選擇的是結點

x[i+1,n-1]

,如何選擇呢?即是從當前剩餘結點中選擇乙個出邊費用最小的結點來走,首先需要判斷剛走過的節點是否與目前要走的節點連通,若連通再看若走此結點後是否可獲得最優值,若比目前最優值

bestc

優,就將其加入到優先佇列中。如此迴圈直到走到倒數第二個結點,此時判斷是否可以構成迴路以及費用是否最優以便取捨。

以上書面寫法:

演算法的while

迴圈體完成對排列樹內部結點的擴充套件。對於當前擴充套件結點,演算法分

2種情況進行處理:

1、首先考慮

s=n-2

的情形,此時當前擴充套件結點是排列樹中某個葉結點的父結點。如果該葉結點相應一條可行迴路且費用小於當前最小費用,則將該葉結點插入到優先佇列中,否則捨去該葉結點。

2、當s時,演算法依次產生當前擴充套件結點的所有兒子結點。由於當前擴充套件結點所相應的路徑是

x[0:s]

,其可行兒子結點是從剩餘頂點

x[s+1:n-1]

中選取的頂點

x[i]

,且(x[s]

,x[i])

是所給有向圖

g中的一條邊。對於當前擴充套件結點的每乙個可行兒子結點,計算出其字首

(x[0:s]

,x[i])

的費用cc

和相應的下界

lcost

。當lcost時,將這個可行兒子結點插入到活結點優先佇列中。 

演算法中while

迴圈的終止條件是排列樹的乙個葉結點成為當前擴充套件結點。當

s=n-1

時,已找到的迴路字首是

x[0:n-1]

,它已包含圖

g的所有

n個頂點。因此,當

s=n-1

時,相應的擴充套件結點表示乙個葉結點。此時該葉結點所相應的迴路的費用等於cc和

lcost

的值。剩餘的活結點的

lcost

值不小於已找到的迴路的費用。它們都不可能導致費用更小的迴路。因此已找到的葉結點所相應的迴路是乙個最小費用旅行售貨員迴路,演算法可以結束。

此處雖然選定了如何比較優先順序,而且出邊不一定構成迴路,但是他一定是乙個下界(或許不可能達到),在程式中每次計算總會找到最優,因為最終的結果中是已走過的地方所需費用,並不包含出邊費用。

程式源**:

#include #include #include using namespace std;

class node ;

class cmp

};class tsp {

private:

int n; //圖的頂點數量

int *a; //圖的鄰接矩陣

int noedge; //無邊標記

int *x ; //當前解

int cc; //當前耗費

int *bestx; //最優解、

int bestc; //最短路長

public:

int solve(int n_,int *a_,int noedge_,int v)

{n = n_;

a = a_;

noedge = noedge_;

priority_queue,cmp> q;

int minout[n+1]; //i節點與其他節點的路徑中最小的耗費

int minsum = 0;

for(int i=1;i<=n;++i) {

int min = noedge;

for(int j=1; j<=n; ++j) {

if(a[i*(n+1)+j]!=noedge && (a[i*(n+1)+j]

演算法入門7 分支限界法

中已經提到過,回溯法的思想是深度優先搜尋加剪枝,與之相對,分支限界法的思想是廣度優先搜尋加剪枝。1.分支限界法 廣度優先搜素 1.簡單概述 分支限界法思路的簡單描述是 把問題的解空間轉化成了圖或者樹的結構表示,然後使用廣度優先搜尋策略進行遍歷,遍歷的過程中記錄和尋找所有可行解或者最優解。基本思想類同...

演算法入門(5) 分支限界法

一 基本描述 類似於回溯法,也是一種在問題的解空間樹t上搜尋問題解的演算法。但在一般情況下,分支限界法與回溯法的求解目標不同。回溯法的求解目標是找出t中滿足約束條件的所有解,而分支限界法的求解目標則是找出滿足約束條件的乙個解,或是在滿足約束條件的解中找出使某一目標函式值達到極大或極小的解,即在某種意...

演算法詳解(五) 分支界限 裝載問題

優先佇列 優先佇列 class node def init self,index,bestw,upbound param index 表示第幾個物品 param upbound 表明當前物品的裝載上限 param bestw 表明最優裝載量 self.index index self.bestw b...