0x08算法设计与分析复习(二):算法设计策略-回溯法2
參考書籍:算法設計與分析——C++語言描述(第二版)
算法設計策略-回溯法
子集和數
問題描述
已知n個不同的正數wi(0≤i≤n?1)的集合,求該集合的所有滿足條件的子集,使得每個子集中的正數之和等于另一個給定的正數M。
回溯法求解
對于子集和數問題問題,可采用兩種不的解結構形式:可變長度元組和固定長度元組。
采用可變長度元組表示解
當采用可變長度元組表示解時,代表一個可行解的元組長度可以不同,成為一個k-元組(x0,x1,?,xk?1),0≤k≤n。元組的每個分量的取值可以是元素值,也可以是選入子集的正數的下標。通常的做法是列出入選子集的元素下標,如果采用這種形式的解,其顯式約束可描述為:xi∈{j|j是整數且0≤j<n}且xi<xi+1(0≤i<n?1)。加入條件xi<xi+1可以避免產生重復子集現象。隱式約束如下:∑k?1i=0wxi=M。
采用固定長度元組表示解
固定長度n-元組(x0,x1,?,xn?1),xi∈{0,1},0≤i<n。xi=0表示wi未選入子集;xi=1表示wi入選子集。采用這種形式的解,其顯示約束可描述為:xi∈{0,1}(0≤i<n?1)。問題的隱式約束是選入子集的正數之和等于M,即∑n?1i=0wixi=M。
一般稱從n個元素的集合中找出滿足某些性質的子集的狀態空間樹為子集樹。子集樹有2n個解狀態,遍歷子集樹的時間為Ω(2n)。
最后討論子集和數的約束函數Bk。假定采用固定長度元組的結結構,已經知道,若約束函數Bk(x0,x1,?,xk)=true,則算法需要進入考察(x0,x1,?,xk)子樹的過程,在本問題中,可以容易想到如下的約束函數:
- 因為如果上式得不到滿足,那么意味著當剩余的正數全部加入子集后,子集中的正數之和仍小于M,自然表明部分向量(x0,x1,?,xk)不可能導致一個答案狀態。
如果假定n個正數wi,0≤i≤n?1已經按非減次序排列,那么還可以在上式約束條件中添加約束條件:
- 因為如果子集中加入下一個待選的正數wk(它是剩余正數中最小的),使整個子集之和已大于M,這顯然意味著從剩余正數中選取任意一個正數加入到現有子集后都不可能使子集和數等于M。
根據上面兩式,可以得到約束函數:
Bk(x0,x1,?,xk)為true,當且僅當:
子集和數算法
函數定義:
void SumOfSub(float s, int k, float r, int *x, float m, float *w)
- 前置條件:wi≤wi+1(0≤i<n?1),s+r≥m&&s+wk≤m(s=∑k?1i=0wixi,r=∑n?1i=kwi)
- 后置條件:在以(x0,x1,?,xk)為根的子樹上搜索答案狀態
函數SumOfSub的初始條件是s=0,r=∑n?1i=0wi≥m和w0≤m。
//子集和數的回溯算法 void SumOfSub(float s, int k, float r, int *x, float m, float *w) {x[k]=1;if(s+w[k]==m){for(int j = 0;j<k;j++){//得到一個可行解cout << x[j]<<" ";}cout << endl;} else if(s+w[k]+w[k+1]<=m){//搜索左子樹SumOfSub(s+w[k],k+1,r-w[k],x,m,w);}if((s+r-w[k]>=m)&&(s+w[k+1]<=m)){x[k]=0;//搜索右子樹SumOfSub(s,k+1,r-w[k],x,m,w);} }void SumOfSub(int *x,int x, float m,float *w) {float r=0;for(int i = 0;i<n;i++){r=r+w[i];//計算r=\sum_{i=k}^{n-1}w[i];}if(r>=m && w[0]<=m)SumOfSub(0,0,r,x,m,w); }圖的著色
問題描述
已知無向圖G=(V,E)和m中不同顏色,如果只允許使用這m中顏色對圖G的結點著色,每個結點著一種顏色,問是否存在一種著色方案,使得圖中任意相鄰的兩個結點都有不同的顏色。這就是m-著色判定問題(m-colorability decision problem)。對給定的無向圖G,求對圖G的結點著色所需的最少顏色數m,使得圖中任意兩個相鄰結點有不同的顏色。這被稱為m-著色最優化問題(m-colorability optimization problem),整數m稱為圖G的著色數(chromatic number)。
回溯法求解
設無向圖G=(V,E)采用如下定義的鄰接矩陣定義:
下面采用n-元組(x0,x1,?,xn?1)表示圖G的m-著色判定問題的解。每個xi∈{1,2,3,?,m},0≤i<n,表示結點的顏色,這是顯式約束。xi=0表示沒有可用的顏色。因此解空間的大小為mn。其隱式約束可描述為:如果邊(i,j)∈E,則xi≠xj。
約束函數的設計:約束函數從隱式約束產生,對所有i和j(0≤i,j<k,i≠j),若a[i][j]=1,則xi≠xj。
圖著色算法
//圖的m-著色算法 template<class T> void MGraph<T>::NextValue(int k,int m,int *x) {//本函數在[1,m]中為x[k]確定一個值最小,且不與其鄰接點沖突的顏色//x[k]=0表示沒有可用顏色,顏色從1開始編號do{x[k]=(x[k]+1)%(m+1);//嘗試下一種顏色if(!x[k])return;//沒有可用顏色for(int j=0;j<k;j++){if(a[k][j] && x[k]==x[j]){//若(i,j)是圖的邊,且相鄰結點k和j顏色相同break;//發生沖突,選擇下一種顏色}}if(j==k)return;//成功選擇一種顏色返回}while(1); }template<class T> void MGraph<T>::mColoring(int k,int m,int *x) {do{NextValue(k,m,x);//為x[k]分配顏色if(!x[k])break;//x[k]=0表示沒有適當的顏色if(k==n-1){//得到圖G的一種m-著色方案for(int i=0;i<n;i++){cout << x[i] <<" ";}cout << endl;} else {//已經對前k個結點分配了顏色,嘗試其余結點mColoring(k+1,m,x);}}while(1); }template<class T> void MGraph<T>::mColoring(int m,int *x) {mColoring(0,m,x); }函數mColoring對一個給定的無向圖G和m,列出圖中結點的所有可能的m-著色方案。算法以深度優先方式生成狀態空間樹中的結點,尋找所有答案結點,即m-著色方案。搜索中使用約束函數剪去不可能包含答案結點的分枝。
時間分析
算法的計算時間上界可以由狀態空間樹的結點樹∑n?1i=0mi來確定。每個結點的處理時間即NextValue的執行時間為O(mn),因此總時間為:
哈密頓環
問題描述
已知圖G=(V,E)是一個n個結點的連通圖。連通圖G的一個哈密頓環(Hamiltonian cycle)是圖G的一個回路,它經過圖中的每一個結點,且只經過一次。一個哈密頓環是從某個結點v0∈V開始,沿著圖G的n條邊環行的一條路徑(v0,v1,?,vn?1,vn),其中,(vi,vi+1)∈E,(0≤i<n),它訪問圖中每個結點且只訪問一次,最后返回開始結點,即除v0=vn外,路徑上其余各點各不相同。并不是每個連通圖都存在哈密頓環。
哈密頓環算法
對于n個結點的圖G=(V,E)的哈密頓環問題,可采用n-元組表示問題的解(x0,x1,?,xn?1)。每個xi∈{0,1,?,n?1},0≤i<n,代表路徑上一個結點的編號,這就是顯式約束。因此解空間的大小為nn。其隱式約束可描述為:xi≠xj(0≤i,j<n,i≠j),且(xi,xi+1)∈E,xi,xi+1∈V(i=0,1,?,n?2),又(xn?1,x0)∈E。
//哈密頓環算法 //,函數NextValue的功能是約束函數。 template<class T> void MGraph<T>::NextValue(int k,int *x) {//函數NextValue的功能是約束函數do{x[k]=(x[k]+1)%n;//下一個結點編號if(!x[k])return;if(a[x[k-1]][x[k]]){//檢查(x[k-1],x[k])是否是圖中一條邊for(int j=0;j<k;j++){//檢查與前k個結點是否相同if(x[j]==x[k])break;}if(j==k){//x[k]是當前可取的結點編號if((k<n-1)||((k==n-1)&&a[x[n-1]][x[0]]))return;}}}while(1); }template<class T> void ExtMGraph<T>::Hamiltonian(int k,int *x) {//遞歸函數Hamiltonian實際計算哈密頓環do{NextValue(k,x);//產生x[k]的下一個值if(!x[k])return;//x[k]=0表示x[k]已經沒有可取的值if(k==n-1){//輸出一個哈密頓環for(int i=0;i<n;i++){cout << x[i]<<" ";}cout << "0" << endl;} else {//深度優先進入下一層Hamiltonian(k+1,x);}}while(1); }template<class T> void ExtMGraph<T>::Hamiltonian(int *x) {Hamiltonian(1,x);//x[0]=0為約定的起始結點 }轉載于:https://www.cnblogs.com/born2run/p/9581367.html
總結
以上是生活随笔為你收集整理的0x08算法设计与分析复习(二):算法设计策略-回溯法2的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 女人梦到黄金是什么意思
- 下一篇: Maven——继承和聚合