Backtracking algorithm (take solving the n queens problem as an example)

Basic idea:

The basic idea of ​​the backtracking algorithm is: go forward from one road, advance if you can, go back if you can't, and try another road. The eight queens problem is a typical backtracking algorithm. The first step is to place a queen in order, and then the second step meets the requirements to place the second queen. If there is no position that meets the requirements, then the position of the first queen must be changed and the second queen will be placed again. The position of the 2 queens, until you find a position that meets the conditions. It is an algorithm with depth-first search and jump search.

To put it bluntly, the backtracking algorithm is an exhaustive method, but in the process of exhaustion, the pruning function is used to skip some unnecessary searches, skip some child nodes that cannot reach the final state, and reduce the number of state space tree nodes generation

Then we are more confused about the backtracking algorithm, what is the state space tree, how to generate the state space tree, how to find out the unsatisfied state according to the problem, and how to set the logic of the pruning function;

Pseudocodes for the two methods of backtracking:

1. Recursive backtracking:

void Backtrack(int t){
    
    
	if(t > n) Output(x);//Output 记录或者输出可行解
	else{
    
    
		for( int i = f(n,t); i <= g(n,t); ++i){
    
    //f(n,t)和g(n,t)表示在当前结点未搜索过的子树的起始编号和终止编号
			x[t] = h(i);
			if(constraint(t) && Bound(t)) Backtrack(t+1);//constraint和bound分别是约束函数和界限函数
		}
	}
}
递归实现回溯非常容易理解,但是执行效率没有迭代高,根据需要,开辟大量的内存空间,来进行递归。

2. Iterative backtracking:

void IterativeBacktrack(void){
    
    
 	int t = 1;
 	while(t > 0){
    
    
 		if(f(n,t) < g(n,t)){
    
    
 			for(int i = f(n,t); i <= g(n,t); ++i){
    
    //这个for 是遍历各个值的意思,实际中写成for循环会有逻辑错误
 				x[t] = h(i);
 				if(constraint(t) && bound(t)){
    
    
 					if(solution(t)) Output(x);//solution 判断是否已经得到问题的解
 					else t++;
 				}
 				else t--;
 			}
 		}
 	}
 }
 //迭代回溯相比较递归比较难理解,它的迭代过程就是解决问题的逻辑过程,不断进行循环,根据问题的规模,有时候时间复杂度也是不容乐观的。

In order to better understand the principle of the backtracking algorithm, let's take solving the n queens problem as an example:

For example: the problem is to place n queens on an n×n grid chess so that they cannot attack each other, that is, any two queens cannot be in the same row, column or slant, how many ways are there .

If this problem is solved by a simple exhaustive method, n^n search results will be found. When exhaustive, start with the scheme that all queens are placed in the first row, and check whether the queens will attack each other. If so, move the queen of column H to verify the next plan. After moving to the end, "carry" to the queen of column G and move one space, and the queen of column H will try all n rows again. There is no doubt that this method is extremely inefficient. If n is too large, the computer will run to death directly. So let's look at the backtracking algorithm.

Before talking about the algorithm, let me show you what a state space tree is;

Here we use the four queens problem to explain what a state space tree is:
insert image description here
take a 4×4 empty chessboard as the root, first place a queen 1 at the first row and first column (1, 1) of the chessboard, and then observe the queen 2 In the case of queen 2, it cannot be placed in the first column and the second column, so try the third column. When we place queen 2 at (2, 3), we find that queen 3 cannot be placed and enters a dead end. When we backtrack, we place queen 2 in the fourth column, and then find that queen 3 can be placed at (3, 2), but at this time the problem arises again, queen 4 cannot be prevented, and it enters a dead end, then we continue to backtrack , go back to the position where queen 1 should be placed, place queen 1 in the second column, and so on, until the last possible placement solution is found. This is the search state space tree for the algorithm.
But the above is only a part, only until Queen 1 reaches the second column, other methods can also have a search state, but the general idea is the same.

The search process is as follows (take the four queens as an example).
insert image description here
What is more fun about solving the queen problem is that each node of its state space tree is a two-dimensional space. When the code is implemented, each step of the search can be clearly performed. Conforms to the logic of computer storage.
So far we have actually figured out that the state of dissatisfaction is that at each queen position, the same diagonal line in the same column is dissatisfaction. Once we encounter this state in the search process, when we search step by step , it is necessary to use the pruning function (in this question is an if judgment) to perform jumping backtracking search.

To sum up, we have roughly figured out what kind of logic the backtracking is when performing backtracking. The difficulty of backtracking is that you need to be clear enough about the problem to figure out whether your space tree is a subset tree or a sorting tree, or something else. How to simulate your search process when implementing code, which data structure to use, whether to use recursion or iterative implementation process. (For common difficult problems, you can first write a recursive pseudocode, which is generally easier to come up with, and then modify it based on the pseudocode) This is our general solution process. In a word, the idea of ​​backtracking is very simple and easy to understand, but when going back to solve the problem, there is still a certain distance in code implementation, which requires us to face a lot of problems and practice more.

Code:

Here only iterative backtracking

#include<stdio.h>
#include<iostream> 
#include<cmath>
using namespace std;
//用一维数组存储,解决行冲突,数组下标为行数,数组值为列数 
int x[10]={
    
    0};
int count=0;
bool judge(int k)//判断第k行某个皇后是否发生冲突 ,发生冲突就跳过
{
    
    
    int i=1;
    while(i<k)  //循环i到k-1之前的皇后; 
    {
    
     
        if(x[i]==x[k]||abs(x[i]-x[k])==abs(i-k))//存在列冲突或者存在对角线冲突(实际上就是一种剪枝)
            return false;
        i++;
    }
    return true;
}
void queen(int n)
{
    
    
    int i,k=1; //k为当前行号,从第一行开始 
    x[1]=0;//x[k]为第k行皇后所放的列号
    while(k>0)
    {
    
    
        x[k]++;  //首先从第一列开始判断 
        while(x[k]<=n&&!judge(k))//推导k行到底选择哪一列,如果当前该列不行则下一列,直到成立即可; 
          x[k]++;
        if(x[k]<=n)  
        {
    
    
            if(k==n)//输出所有解 
            {
    
    
                for(i=1;i<=n;i++)
                    printf("第%d行的皇后:在第%d列  ",i,x[i]);
                count++;
                printf("-------这是其中第%d个解",count);   
                printf("\n");
            }

            else//判断下一行
            {
    
    
                k++; x[k]=0;
            }
        }
        else k--;//没找到,回溯
    }
    return ;
}
int main()
{
    
    
    int n;
    cout<<"请输入你的n皇后:     "; 
    cin>>n;//n最大值不超过10 
    queen(n);
    return 0;
}

Guess you like

Origin blog.csdn.net/A52091/article/details/105724618
Recommended