递归回溯法

递归回溯法

今天数据结构课上讲到了递归回溯法,用到了三个著名的例子。便想着干出来来加深下理解

深度优先搜索

这个当初学完离散数学就写了个出来。原理简单,就是一直往前冲,没路就回溯,看之前走过的地方有没有别的方向可以走。有实时打印的功能方便看走迷宫的实时情况,还用栈实现了打印全称路径。直接上代码

#include <iostream>
#include <stack>
#include <windows.h>
using namespace std;

struct point{
    int x;
    int y;
};//坐标结构体

const int dx[]={0,1,0,-1};
const int dy[]={-1,0,1,0};//down right up left;

const int MAXROW=10,MAXCOLUMN=10;//迷宫长宽
//标记为0的点为可走,1的点不可走,2的点已经走过
int maze[MAXROW][MAXCOLUMN]={
        1,1,1,1,1,1,1,1,1,1,
        0,0,0,1,1,1,1,1,1,1,
        1,1,0,1,1,1,1,1,1,1,
        1,1,0,0,0,0,1,1,1,1,
        1,1,0,1,1,0,1,1,1,1,
        1,1,0,1,1,0,1,1,1,1,
        1,1,1,1,1,0,1,1,1,1,
        1,1,1,1,1,0,0,0,1,1,
        1,1,1,1,1,1,1,0,0,0,
        1,1,1,1,1,1,1,1,1,1,

};

void displayMaze(){
    for(int i=0;i<MAXROW;i++){
        for(int j=0;j<MAXCOLUMN;j++){
            if(maze[i][j]==1){
                cout<<" *";
            }
            else if(maze[i][j]==0){
                cout<<"  ";
            }
            else{
                cout<<" #";
            }
        }
        cout<<'\n';
    }
    cout<<" ====================\n";
}

point sp={1,0},ep={8,9};//记录起始点和终点的坐标
point prepts[MAXROW][MAXCOLUMN]={-1,-1};//用来记录当前坐标的前一个坐标

void printpath(){
    stack<point> s1;//用栈先进后出的特点,从终点倒序将点压入栈,直到起点,然后弹栈
    point temp=ep;
    do{
        s1.push(temp);
        temp=prepts[temp.x][temp.y];
    }while(!(temp.x==sp.x&&temp.y==sp.y));
        s1.push(temp);
    while(!s1.empty()){
        temp=s1.top();
        s1.pop();
        cout<<"("<<temp.x<<", "<<temp.y<<") ";
    }
    cout<<'\n';
}
bool flag=0;
void dfs(point now){
    maze[now.x][now.y]=2;
    Sleep(500);
    system("cls");
    displayMaze();

    if(now.x==ep.x&&now.y==ep.y){
        cout<<"find the path!"<<endl;
        flag=1;
        return;
    }

    else{
        for(int i=0;i<3;i++){
            point nxt{now.x+dx[i],now.y+dy[i]};
            if(maze[nxt.x][nxt.y]==0&&nxt.x>=0&&nxt.x<=MAXROW&&nxt.y>=0&&nxt.y<=MAXCOLUMN){
            prepts[nxt.x][nxt.y]=now;//追踪最终路径肯定要从终点往前追踪,起点开始会有很多条路,不好区分
            dfs(nxt);
            }
        }
   }
}

int main()
{
    dfs(sp);
    if(flag)
        printpath();
    else
        cout<<"fail to find the end!";
    return 0;
}

实际运行效果

图着色问题

这个原理和八皇后类似,先从第一点开始递归下去上色,如果遇到颜色不够发生同色冲突时,再回溯,先前的点换个颜色后再继续递归,直到最后一个点被上色完成(最后一个上完后也会回溯,就是寻找另一种上色方法了)。但如何检测每个点是否与相邻的点的颜色相同呢,困扰了我挺久。后来想起邻接矩阵,用一个二维数组就可以表示清点与点之间的相邻关系了,再用一个数组去表示每个点的颜色就行了。

#include <iostream>
#include <string>
using namespace std;

const int m=4,n=5;//m:color,n:vertexes
int counts=0;//the count of number of methods of coloring each vertex
int adjMatrix[5][5]{
0 ,1 ,1 ,1 ,0 ,
1 ,0 ,1 ,1 ,1 ,
1 ,1 ,0 ,1 ,0 ,
1 ,1 ,1 ,0 ,1 ,
0 ,1 ,0 ,1 ,0
};

int color[n];//the color of each vertex

string colorDescribe[m]{"red","blue","yellow","green"};

//check whether the color of two adjacent vertexes is the same
//1:not the same
bool check(int x){
    for(int i=0;i<x-1;i++)//i<x-1, no need to check vertexes that haven't been colored
        if(adjMatrix[x-1][i]==1&&color[i]==color[x-1]) return 0;

        return 1;
}

void colorGraph(int vertex){
    //n+1,画到第六个点时说明前五个都上色成功
    if(vertex==n+1){
        counts++;
        cout<<"Method "<<counts<<": ";
        for(int i=0;i<n;i++)
            cout<<"V"<<i+1<<": "<<colorDescribe[color[i]-1]<<", ";
        cout<<endl;
        return;
    }
    else{
        for(int i=1;i<m+1;i++){
            color[vertex-1]=i;
            if(check(vertex)){
                colorGraph(vertex+1);
            }
            color[vertex-1]=0;//当前点颜色清空,提供回溯条件
        }
    }
}

int main()
{
    colorGraph(1);//从一号点开始
    cout<<counts;
    return 0;
}

代码用的是4个颜色,五个点的情况

n-皇后问题

大名鼎鼎的八皇后问题,每个皇后的横排竖排斜排都不能有其他皇后存在。这个方法与上色同理。先放好一个皇后,再去放第二个皇后,检测横排竖排斜排是否有其他皇后。如果递归到了死局,怎么放都会冲突,就回溯(当然到达了base case也会回溯,就是寻找下一种摆法了),改变先前的皇后位置,以新面貌继续进行递归。

检测那里出错了好久,我一开始傻了,只检测当前皇后的上面一行,或者一个斜格,后来想想就算第一个也会与第八个在一个斜线上,检测时应该是要考虑当前行数的。

#include <iostream>

using namespace std;


const int n=8;//8 queens
int board[n][n];//8x8 chessboard
int counts=0;//counts of methods

struct queen{
    int x;
    int y;
};

queen q[n];//the number of the queens

//Check whether has other queens on diagonal and vertical lines
//need to make sure the array index won't out of bounds
bool check(queen k){
    for(int row=k.x;row>0;row--){

        if(k.x>=0&&k.y>0&&k.y<n-1){
            if(board[k.x-row][k.y]==1|| ( k.y-row>=0 && board[k.x-row][k.y-row]==1) || (k.y+row<=n-1 &&board[k.x-row][k.y+row]==1))
                return 0;
        }
        else if(k.y==0){
            if(board[k.x-row][k.y]==1 || (k.y+row<=n-1 && board[k.x-row][k.y+row]==1))
                return 0;
        }
        else if(k.y==n-1){
            if( board[k.x-row][k.y]==1 ||( k.y-row>=0 && board[k.x-row][k.y-row]==1))
                return 0;
        }
    }

    return 1;
}

//traceback
void n_queen(int row){
    //base case: when reach the 9th row, one method is found,recursion stop
    if(row==n+1){
        counts+=1;
        return;
    }
    
    else{
        for(int col=0;col<n;col++){
            q[row-1].x=row-1;
            q[row-1].y=col;
            board[row-1][col]=1;//1:this position has a queen
            if(check(q[row-1])){
                n_queen(row+1);
            }
            board[row-1][col]=0;
        }
    }
}

int main()
{
    n_queen(1);//start from the first row
    cout<<"For "<<n<<" queens, there are "<<counts<<" different methods to put.";
    return 0;
}

8个的情况是92种,程序应该是没问题的。

猜你喜欢

转载自www.cnblogs.com/dreamyt/p/di-gui-hui-su-fa.html