算法笔记之回溯法(2)
著色問題
問題分析
假設地圖共有7個區域,分別是A/B/C/D/E/F/G,對上面順序進行編號,每個區域用一個結點表示,相鄰的區域有連線,那么地圖就轉化成一個無向連接圖。
算法設計
搜索解空間
- 約束條件:假設當前擴展結點位于解空間樹的第t層,那么從第1到第t-1層的結點情況都已經確定,接下來是按照擴展結點的第一個分支進行擴展,此時需要判斷是否將第t個結點著色情況。第t個結點的色號要與前t-1個結點中與其有邊相連的結點顏色不同,如果有顏色相同的,則第t個結點不能用這個色號,換下一個色號嘗試。
- 限界條件:無。
- 搜索過程:擴展結點沿著第一個分支擴展,判斷約束條件,滿足則進入深一層繼續搜索;如果不滿足,則擴展生成的結點被剪掉,換下一個色號嘗試。如果所有色號都嘗試完畢,該結點變成死結點,向上回溯到距離其最近的活結點,繼續搜索。搜索到葉子結點時,找到一種著色方案,搜索過程直到全部活結點變成死結點為止。
解題過程
地圖7個區域,3種顏色。
代碼實現
//約束條件 bool isRight(int t) {for (int j = 1; j < t; j++){if (map[t][j]){if (x[j] == x[t])return false;}}return true; }//回溯方法函數 void Backtrack(int t) {if (t > n){sum++;cout << "第" << sum << "種方案:";for (int i = 1; i <= n; i++)//輸出該著色方案{cout << x[i] << " ";}cout << endl;}else {for (int i = 1; i <= m; i++){x[t] = i;if (isRight(t))Backtrack(t + 1);}} }算法復雜度分析
n皇后問題
問題介紹
在n×n的棋盤上放置彼此不受攻擊的n個皇后。按照國際象棋規則,皇后可以攻擊與之在同一行、同一列、同一斜線上的棋子。現在在n*n的棋盤上放置n個皇后,使其不受攻擊。
問題分析
求解策略:
以行為主導:
- 在第1行第1列放置第一個皇后。
- 在第2行放置第2個皇后。第2個皇后的位置不能和前面的皇后同列、同斜線,不用再判斷同行了,因為每行我們本來就只放一個。
- 在第3行放置第3個皇后。第3個皇后的位置不能和前面的皇后同列、同斜線。
- ……
- 在第t行放置第t個皇后。第t個皇后的位置不能和前面的皇后同列、同斜線。
- ……
- 在第n行放置第n個皇后。第n個皇后的位置不能和前面的皇后同列、同斜線。
算法設計
(1)定義問題的解空間。n皇后問題解的形式為n元組:{x1,x2,...,xi,...,xn},分量xi表示第i個皇后放置在第i行第xi列,xi取值為1,2,3,...,n。顯約束為不同行。
(2)解空間的組織結構:一顆m(m=n)叉樹,樹深度為n。
(3)搜索解空間。
約束條件:在第t行放置第t個皇后時,第t個皇后的位置不能和前t-1個皇后同列、同斜線。第i個皇后和第j個皇后不同列,即xi!=xj。
限界條件:不需要設置。
搜索過程:
從根開始,以DFS的方式進行搜索。根節點是活結點,并且是當前的擴展結點。在搜索過程中,當前的擴展結點沿縱深方向移向一個新結點,判斷該新結點是否滿足隱約束。如果滿足,則該新結點成為活結點,并且成為當前的擴展結點,繼續深一層的搜索;如果不滿足,則換到該新結點的兄弟結點繼續搜索;如果新結點沒有兄弟結點,或其兄弟結點已全部搜索完畢,則擴展結點成為死結點,搜索回溯到其父結點處繼續進行。搜索過程直到找到問題的根結點變成死結點為止。
代碼實現
bool isPlace(int t) {bool place = true;for (int j = 1; j < t; j++){if (x[t] == x[j] || t - j == fabs(x[t] - x[j]))//判斷列、對角線是否沖突{place = false;break;}}return place; }void backtrack(int t) {if (t > n){countn++;for (int i = 1; i <= n; i++){cout << x[i] << " ";}cout << endl;cout << "---------" << endl;}else{//分別判斷n個分支,特別注意i不要定義為全局變量,否則遞歸調用有問題for (int i = 1; i <= n; i++){x[t] = i;if (isPlace(t))backtrack(t + 1);//上面說的是不沖突就進行下一行搜索}} }算法復雜度分析
最優加工順序
問題描述
現在有3個機器零件{J1,J2,J3},在第一臺機器上的加工時間分別為2、5、4,在第二臺機器上的加工時間分別為3、1、6.如何安排零件加工順序,使第一個零件從機器1上加工開始到最后一個零件在機器2上加工完成,所需的總加工時間最短?
問題分析
我們通過分析可以發現,第一臺機器可以連續加工,而第二臺機器開始加工的時間是當前第一臺機器的下線時間和第二臺機器下線時間的最大值。
實際上就是找到n個機器零件的一個排列,使總的加工時間最短。
算法設計
搜索解空間。
- 約束條件:無約束條件。
- 限界條件:用f2表示當前已完成的零件在第二臺機器加工結束所用的時間,用bestf表示當前找到的最優加工方案的完成時間。顯然,繼續向深處搜索時,f2不會減少,只會增加。因此,當f2≥bestf時,沒有繼續向深處搜索的必要。限界條件可以描述為:f2。f2初值為0,bestf的初值為無窮大。
代碼實現
1.數據結構
struct node {//機器零件在第一臺機器上的加工時間x和第二胎機器上的加工時間yint x,y; }T[MAX];2.按限界條件進行搜索求解:t表示當前擴展結點在第t層,f1表示當前第一臺機器上加工的完成時間,f2表示當前第二臺機器上加工的完成時間。如果t>n表示已經到達葉子結點,記錄最優值和最優解,返回。否則,分別判斷每個分支是否滿足約束條件,若滿足則進入下一層backtrack(t+1);如果不滿足則反操作復位,考察下一個分支(兄弟結點)。
void Backtrack(int t) {if(t>n){for(int i=1;i<=n;i++)bestx[i]=x[i];//記錄最優隊列bestf=f2;//更新最優值return ;}for(int i=t;i<=n;i++){f1+=T[x[i].x;int temp=f2;f2=max(f1,f2)+T[x[i]].y;if(f2<bestf)//滿足限界條件{swap(x[t],x[i]);//交換Backtrack(t+1);//繼續搜索swap(x[t],x[i]);//復位,反操作}f1-=T[x[i]].x;//復位,反操作f2=temp;//復位,反操作} }算法復雜度分析
時間復雜度為O(nn!)≈O((n+1)!),空間復雜度為O(n)。
算法優化改進
新的算法的時間復雜度為O(nlogn),空間復雜度為O(n)。利用貝爾曼規則,代碼如下:
#include<iostream> #include<algorithm> using namespace std ; const int MX=10000+5 ; int n; struct node {int id;int x,y; }T[MX] ; bool cmp(node a,node b) {return min(b.x,a.y)>=min(b.y,a.x);//按照貝爾曼規則排序 } int main() {cout<<"請輸入機器零件的個數 n:";cin>>n;cout<<"請依次輸入每個機器零件在第一臺機器上的加工時間x和第二臺機器上的加工時間y:";for(int i=0;i<n;i++){cin>>T[i].x>>T[i].y;T[i].id=i+1;}sort(T,T+n,cmp); //排序int f1=0,f2=0;for(int i=0;i<n;i++) //計算總時間{f1+=T[i].x;f2=max(f1,f2)+T[i].y;}cout<<"最優的機器零件加工順序為:";for(int i=0;i<n;i++) //輸出最優加工順序cout<<T[i].id<<" ";cout<<endl;cout<<"最優的機器零件加工的時間為:";cout<<f2<<endl;return 0 ; }總結
以上是生活随笔為你收集整理的算法笔记之回溯法(2)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: MQ框架的比较
- 下一篇: java基础(一):谈谈java内存管理