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