回溯算法

回溯(backtracking):

有“通用解題法”之稱。用它可以系統地搜索問題的所有解。它在問題的解空間樹中,按深度優先策略,從根結點出發搜索解空間樹。算法搜索至解空間樹的任一結點時,先判斷該結點是否包含問題的解,如果肯定不包含,則跳過對以該節點為根的子樹的搜索,逐層向其祖先結點回溯;否則,進入該子樹,繼續按深度優先策略繼續搜索。

八皇后問題:

問題描述:在8×8的國際象棋棋盤上放置八個皇后,使得任何一個皇后都無法直接吃掉其他的皇后?為了達到此目的,任兩個皇后都不能處于同一條橫行、縱行或斜線上。

#include <iostream>
#include <cmath>
#define N 8
using namespace std;
int a[8];//皇后每行每列有且只能有一個,所以用a[i]代表第i個皇后(在第i行)放在第a[i]列
int cnt=1;//記錄解的序號
void search(int m);
int canPlace(int row ,int col);
void printResult();
/* run this program using the console pauser or add your own getch, system("pause") or input loop */

int main(int argc, char** argv) {
    search(0);
    return 0;
}
void search(int m)
{
    if(m>=N)printResult();
    else
    {
        int i;
        for(i=0;i<N;i++)//枚舉8列
        {
            if(canPlace(m,i))
            {
                a[m]=i;
                search(m+1); 
            }
            a[m]=0;
            
        }
    }
}
int canPlace(int row ,int col)
{
    int i;
    for(i=0;i<row;i++) //皇后肯定不會同行,a[i]==col檢查是否同列,abs(i-row)==abs(a[i]-col)檢查是否同對角線,如果同對角線,行差和列差的絕對值應該相等。
    {
        if(a[i]==col||abs(i-row)==abs(a[i]-col))return 0;
    }
    return 1;
    
    
}
void printResult()
{
    int i,j;
    cout<<"No "<<cnt<<":"<<endl;
    for(i=0;i<N;++i)
    {
        for(j=0;j<a[i];++j)
        {
            cout<<".";
        }
        cout<<"A";
        j++;
        for(;j<N;++j)
        {
            cout<<".";
        }
        cout<<endl;
    }
    cnt++;
    
}

下面給出4皇后回溯過程:


以及八皇后回溯過程(樹太深,所以把時間設置的很快,大家看個熱鬧就行,感受一下計算機的力量):


0-1背包問題:

問題描述:在0 / 1背包問題中,需對容量為c 的背包進行裝載。從n 個物品中選取裝入背包的物品,每件物品i 的重量為wi ,價值為pi 。對于可行的背包裝載,背包中物品的總重量不能超過背包的容量,最佳裝載是指所裝入的物品價值最高。

#include <iostream>
#define MAX 10
using namespace std;
int n;//物品個數
int c;//背包容量
int maxValue;//記錄所選物品最大價值
int w[MAX];//物品重量數組
int v[MAX];//物品價值數組
int a[MAX];//a數組存放當前物品的選擇情況
          //a[i]=0表述不選第i個物品,a[i]=1表示選擇第i個物品
int result[100];
int cnt; 
void init();
void readData();
void search(int m);
void checkMax();
void printResult();
/* run this program using the console pauser or add your own getch, system("pause") or input loop */

int main(int argc, char** argv) {
    cin>>n>>c;
    while(n!=0||c!=0)
    {
        init();
        readData();
        search(0);
        result[cnt]=maxValue;
        cnt++;
        cin>>n>>c;
    }
    printResult();
    return 0;
}

void init()
{
    maxValue=0;
    int i;
    for(i=0;i<MAX;++i)
    {
        w[i]=v[i]=a[i]=0;
    }
}

void readData()
{
    int i;
    for(i=0;i<n;i++)
    {
        cin>>w[i];
    } 
    for(i=0;i<n;i++)
    {
        cin>>v[i];
    } 
    
}

void search(int m)
{
    if(m>=n)
        checkMax();
    else
    {
        a[m]=0;//不要該物品
        search(m+1);
        a[m]=1;//要該物品
        search(m+1);
    }
    
}

void checkMax()
{
    int weight=0,value=0;
    int i;
    for(i=0;i<n;i++)
    {
        if(a[i]==1)
        {
            weight+=w[i];
            value+=v[i];
        }
        if(weight<=c)
        {
            if(value>maxValue)
                maxValue=value;
        }
    }
}

void printResult()
{
    int i;
    for(i=0;i<cnt;++i)
    {
        cout<<result[i]<<endl;
    }
}

堡壘問題:

如圖城堡是一個4×4的方格,為了保衛城堡,現需要在某些格子里修建一些堡壘。城堡中的某些格子是墻,其余格子都是空格,堡壘只能建在空格里,每個堡壘都可以向上下左右四個方向射擊,如果兩個堡壘在同一行或同一列,且中間沒有墻相隔,則兩個堡壘都會把對方打掉。問對于給定的一種狀態,最多能夠修建幾個堡壘。

#include <iostream>
#define MAX 5
int n;
int a[MAX*MAX];
int maxnum;
using namespace std;
void search(int m);
int canPlace(int m);
void checkMax();
/*run this program using the console pauser or add your own getch, system("pause") or input loop*/ 

int main(int argc, char** argv) {
    while(cin>>n&&n!=0)
    {
        
        maxnum=0;
        char c;
        int i;
        for(i=0;i<n*n;++i)
        {
            cin>>c;
            if(c=='.')a[i]=1;
            else if(c=='X')a[i]=0;
        }
        search(0);
        cout<<maxnum<<endl;
        
    }
    return 0;
}

void search(int m)
{
    if(m>=n*n)checkMax();
    else
    {
        search(m+1);
        if(canPlace(m)==1)
        {
            a[m]=2;//放堡壘
            search(m+1);
            a[m]=1;//把堡壘拿掉 
        }
    }
}

int canPlace(int m)
{
    if(a[m]==0)return 0;
    int row,col;
    row=m/n; 
    col=m%n;
    int i,j;
    for(i=0;i<m;++i)
    {
        if(a[i]==2)
        {
            if(i/n==row)//同行有堡壘
            {
                int flag=0;
                for(j=i+1;j<m;++j)
                {
                    if(a[j]==0)flag=1;
                }
                if(flag==0)return 0;                
            }
            if(i%n==col)//同列有堡壘
            {
                int flag=0;
                for(j=col;j<m;j+=n)
                {
                    if(a[j]==0)flag=1;
                }
                if(flag==0)return 0;
            } 
        }
        
    }
    return 1;
}

void checkMax()
{
    int i;
    int cnt=0;
    for(i=0;i<n*n;++i)
    {
        if(a[i]==2)cnt++;
    }
    if(cnt>maxnum)maxnum=cnt;
}

素數環問題:

問題描述:把從1到20這20個數擺成一個環,要求相鄰的兩個數的和是一個素數。

#include <stdio.h>
#include <cmath>
void search(int);
void init();              
void printresult();      
int isprime(int);         
void swap(int,int);       
int a[21];                
int main()
{
    init();
    search(2);            
}
int isprime(int num)
{
    int i,k;
    k=sqrt(num);
    for(i=2;i<=k;i++)
        if(num%i==0)
            return(0);
    return(1);
}
void printresult()
{
    int i;
    for(i=1;i<=20;i++)
        printf("%3d",a[i]);
    printf("\n");
}
void search(int m)
{
    int i;
    if(m>20)                       
    {
        if(isprime(a[1]+a[20]))        //判斷首尾是否滿足要求
            printresult();            
        return;
    }
    else
    {
        for(i=m;i<=20;i++)     //注意i是從m開始的而不是m+1      
        {
            swap(m,i);              
            if(isprime(a[m-1]+a[m]))  
                search(m+1);       
            swap(m,i);             
        }
    }
}
void swap(int m, int i)
{
    int t;
    t=a[m];
    a[m]=a[i];
    a[i]=t;
}
void init()
{
    int i;
    for(i=0;i<21;i++)
        a[i]=i;
}


迷宮問題:

問題描述:給一個20×20的迷宮、起點坐標和終點坐標,問從起點是否能到達終點。
輸入:多個測例。輸入的第一行是一個整數n,表示測例的個數。接下來是n個測例,每個測例占21行,第一行四個整數x1,y1,x2,y2是起止點的位置(坐標從零開始),(x1,y1)是起點,(x2,y2)是終點。下面20行每行20個字符,’.’表示空格;’X’表示墻。
輸出:每個測例的輸出占一行,輸出Yes或No。

#include <iostream>
using namespace std;
#define N 20//20*20的迷宮
int k=20,t=20;//行數和列數
int a[N][N];//1代表墻,0代表可走,2代表已經走過
int n;//測例個數
int startRow,startCol;//入口坐標
int endRow,endCol;//出口坐標
int flag=0;
/* run this program using the console pauser or add your own getch, system("pause") or input loop */
void readData();
void search(int row,int col);
int canPlace(int row,int col);
void printResult();

int main(int argc, char** argv) {
    cin>>n;
    while(n--)
    {
        flag=0;
        readData();
        search(startRow,startCol);
        printResult();
        
    }
    return 0;
}
void readData()
{
    cin>>startRow>>startCol;
    cin>>endRow>>endCol;
    int i,j;
    char c;
    for(i=0;i<k;i++)
    {
        for(j=0;j<t;j++)
        {
            cin>>c;
            if(c=='.')a[i][j]=0;
            else if(c=='X')a[i][j]=1;
        }
    }
    
}
void search(int row,int col)
{
    if((row==endRow)&&(col==endCol))
    {
        flag=1;
        return;
    }
    else
    {
        int r,c;
        a[row][col]=2;//標記已經走過的位置
        r=row,c=col-1;//向左走
        if(canPlace(r,c))search(r,c);
        r=row,c=col+1;//向右走 
        if(canPlace(r,c))search(r,c);
        r=row-1,c=col;//向上走 
        if(canPlace(r,c))search(r,c);
        r=row+1,c=col;//向下走 
        if(canPlace(r,c))search(r,c);
    }
    
}
int canPlace(int row,int col)//判斷是否可走
{
    if(row>=0&&row<k&&col>=0&&col<t&&a[row][col]==0)return 1;//不越界且可以走
    else
        return 0;
}
void printResult()
{
    if(flag==1)cout<<"Yes"<<endl;
    else cout<<"No"<<endl;
}


農場灌溉問題:

問題描述:一農場由圖所示的十一種小方塊組成,藍色線條為灌溉渠。若相鄰兩塊的灌溉渠相連則只需一口水井灌溉。給出若干由字母表示的最大不超過50×50具體由(m,n)表示,的農場圖,編程求出最小需要打的井數。每個測例的輸出占一行。當M=N=-1時結束程序。


#include <iostream>  
using namespace std;  
int m, n;  
  
struct Maze  
{  
    char ch;                     //方塊類型 
    int visited;                 //訪問標記 
    int left, right, up, down;   //連通線  
}maze[100][100];  
int dir[4][2] = {{1,0}, {-1,0}, {0,-1}, {0,1}}; //上下左右四個方向
  
bool Place(int x, int y, int nx, int ny)  
{  
    //未越界且未被訪問過 
    if(nx>0 && nx<=m && ny>0 && ny<=n && !maze[nx][ny].visited)  
    {  
        //向左
        if(nx==x && ny==y-1 && maze[x][y].left && maze[nx][ny].right)  
            return true;  
        //向下 
        if(nx==x+1 && ny==y && maze[x][y].down && maze[nx][ny].up)  
            return true;  
        //向右  
        if(nx==x && ny==y+1 && maze[x][y].right && maze[nx][ny].left)  
            return true;  
        //向上 
        if(nx==x-1 && ny==y && maze[x][y].up && maze[nx][ny].down)  
            return true;  
    }  
    return 0;  
}  
void dfs(int x, int y)  
{  
    maze[x][y].visited = 1;               
    for(int i = 0; i < 4; i++)  
    {  
        int nx = x+dir[i][0];  
        int ny = y+dir[i][1];  
        //將所有可聯通的區域全部標記 
        if(Place(x, y, nx, ny))  
            dfs(nx, ny);  
    }  
}  
int main()  
{  
    while(cin >> m >> n && (m!=-1 || n!=-1))  
    {  
        int cnt = 0;  
        for(int i = 1; i <= m; i++)  
            for(int j = 1; j <= n; j++)  
            {  
                cin >> maze[i][j].ch;  
                //初始化 
                maze[i][j].visited = 0;  
                maze[i][j].down = 0;  
                maze[i][j].up = 0;  
                maze[i][j].right = 0;  
                maze[i][j].left = 0;  
                switch(maze[i][j].ch)  
                {  
                    case'A':    maze[i][j].left=1;  
                                maze[i][j].up=1;  
                                break;  
                    case'B':    maze[i][j].right=1;  
                                maze[i][j].up=1;  
                                break;  
                    case'C':    maze[i][j].left=1;  
                                maze[i][j].down=1;  
                                break;  
                    case'D':    maze[i][j].right=1;  
                                maze[i][j].down=1;  
                                break;  
                    case'E':    maze[i][j].up=1;  
                                maze[i][j].down=1;  
                                break;  
                    case'F':    maze[i][j].left=1;  
                                maze[i][j].right=1;  
                                break;  
                    case'G':    maze[i][j].left=1;  
                                maze[i][j].right=1;  
                                maze[i][j].up=1;  
                                break;  
                    case'H':    maze[i][j].left=1;  
                                maze[i][j].up=1;  
                                maze[i][j].down=1;  
                                break;  
                    case'I':    maze[i][j].left=1;  
                                maze[i][j].right=1;  
                                maze[i][j].down=1;  
                                break;  
                    case'J':    maze[i][j].right=1;  
                                maze[i][j].up=1;  
                                maze[i][j].down=1;  
                                break;  
                    case'K':    maze[i][j].left=1;  
                                maze[i][j].right=1;  
                                maze[i][j].up=1;  
                                maze[i][j].down=1;  
                                break;  
                }  
            }  
        //連通區域個數  
        for(int i = 1; i <= m; i++)  
            for(int j = 1; j <= n; j++)  
            {  
                if(maze[i][j].visited == 0)     // 未被訪問過
                {  
                    dfs(i, j);  
                    cnt ++;  
                }  
            }  
        cout << cnt << endl;  
    }  
    return 0;  
}  

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,702評論 6 534
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,615評論 3 419
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事?!?“怎么了?”我有些...
    開封第一講書人閱讀 176,606評論 0 376
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,044評論 1 314
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,826評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,227評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,307評論 3 442
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,447評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,992評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,807評論 3 355
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,001評論 1 370
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,550評論 5 361
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,243評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,667評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,930評論 1 287
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,709評論 3 393
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,996評論 2 374

推薦閱讀更多精彩內容

  • 1.基本概念 回溯算法實際上一個類似枚舉的搜索嘗試過程,主要是在搜索嘗試過程中尋找問題的解,當發現已不滿足求解條件...
    RavenX閱讀 8,295評論 1 2
  • 貪心算法 先來比較一下貪心算法和動態規劃 貪心算法是指在對問題求解時,總是做出在當前看來是最好的選擇,不考慮整體,...
    Moonsmile閱讀 2,799評論 0 1
  • 引言:這道題目老師強調了肯定要考,所以只有硬著頭皮將其復習了;下面是自己學習回溯算法的學習,僅供參考;一:基本概念...
    cp_insist閱讀 8,603評論 4 3
  • 回溯算法 主要思想 回溯算法的基本思想是:從一條路往前走,能進則進,不能進則退回來,換一條路再試。八皇后問題就是回...
    愛撒謊的男孩閱讀 1,107評論 0 3
  • 回溯法 回溯法的算法框架 1. 綜述 從問題的 解空間樹 中,按照 深度優先 的策略,從根節點出發搜索解空間樹。 ...
    冰源閱讀 570評論 0 0