Detailed ideas for binary optimization of the N queens problem

The topic, the conventional solution (DFS), will not be repeated here. . .

straight to the point

As we all know, Queen N is an NP-complete problem , and if n is slightly larger, the solution process will become very long.

It is difficult to make a qualitative breakthrough in the algorithm, but it might as well work on other details.

All the conventional solutions use an array as a mark, and perform a backtracking algorithm for each layer. In each operation cycle, nothing more than the following things are done:

update element

Judgment element

React

There is basically no room for improvement in updating elements (updating more than one at a time?? It seems to be more complicated). . .

There is also nothing to improve in the response (make a predictive response?? It is also complicated, let’s not talk about it). . .

Obviously, what has room for improvement is the element of determination .

First, consider storage :

We use three arrays to mark the occupancy of two slashes and one column respectively (here, "occupancy" is used to indicate whether a piece can be placed here, and if it is 'occupied', the chess piece cannot be placed)

Can it be combined into an array? Then this array must be able to represent three pieces of information: the occupancy of a column with two slashes at the current position. Sometimes even if it can be expressed, new information must be obtained when moving to the next position. . Doesn't seem feasible.

(So ​​we started boringly simulating the algorithm process on paper again...)

Hey, then you will find that when performing DFS, for example, when you go to the kth layer and put down a chess piece, then we have to go to the next layer to continue the judgment. At this moment, the information we use is the k+1th layer Occupancy, and the occupancy of k+1 is directly affected by the situation of the kth layer, as shown in the figure below:

 We will find that chess pieces cannot be placed in the three positions of the third layer of coloring. 

In addition to the influence of the kth layer, the first k-1 layer also has constraints on the k+1th layer:

Looking back at the classic algorithm, each mark directly negates the position on a line. Let’s jump out of this thinking, can we "step by step" and update the occupancy information of the next line while entering the next line? If so, an array will suffice.  

Thinking about it again, there is already an array (equivalent to a string of numbers), and each grid has two states: with chess pieces and without chess pieces.

Obviously, binary is the way to go! And the efficiency of binary operations is relatively high!

The general idea came out, and the last difficulty is how to safely transfer the occupancy information of the first k-1 layer to the k+1th row. .

It is not difficult to find this rule after trying it out : a chess piece will produce three kinds of influence effects. If it is affected obliquely, it will be offset by one grid in the corresponding direction when it reaches the next layer; if it is affected vertically, it will be moved to the next layer. "Occupy" is still in place.

As shown in the picture:

At this time, there are three types of information updates, and a string of binary is difficult to achieve. . . Then three strings, it is still not a loss compared to the array.

The big logical knot was basically solved in my mind, and I started to change the code below:

First of all, we don't need an array, it is enough to change it to an int. Secondly, considering the characteristics of each information update, we can use the formal parameters of the function to pass it, saving a global variable and making people uncomfortable.

The function header sub-sub:

void Dfs(int i,int left,int col,int right)//分别是 层数  反斜杠mark  一列mark  斜杠mark 

Then it is still the framework of the general DFS solution, only need to replace the place judged by the bool array mark with the binary method. 

Put the code:

void Dfs(int i,int left,int col,int right)//分别是 层数  反斜杠mark  一列mark  斜杠mark
{
    if (i > N)              //判断是否已经枚举完了N行
    {
        showOneSolution();  //枚举完了就输出(此刻我们处于N+1行)
        return;             //返回到第N行
    }
    for (int j = N-1; j >=0; --j) //为啥J倒过来数呢?方便下面if中的偏移判断
    {
        if (!(((left|col|right)>>j)&1))//(自己解读,有助于提升能力 (好吧我懒))
        {
            ans[i] = N-j;
            Dfs(i + 1,(left|(1<<j))<<1,(col|(1<<j)),(right|(1<<j))>>1);  //更新“占用信息”的工作就放在参数传递这里了
                                              //这样子的好处就是,回溯的时候不用做什么恢复处理,
                                              //因为这一层的东西基本没变动
        }
    }
}

So many parentheses are nested. . Oops I know this is poor readability, don't advocate it!

But we are now studying and not working . Try to read some strange codes to help improve your ability to read codes (you can make excuses...)

Ok, put the complete code below:

#include<iostream>

using namespace std;

int total = 0;
int N = 0;
int ans[64] = { 0 }; //结果还是要用数组存滴
void showOneSolution()//用来显示一个解
{
    total++;
    for (int i = 1; i<=N; ++i)
    {
        cout << ans[i] << " ";
    }
        cout << endl;
}
void Dfs(int i,int left,int col,int right)//分别是 层数  反斜杠mark  一列mark  斜杠mark
{
    if (i > N)              //判断是否已经枚举完了N行
    {
        showOneSolution();  //枚举完了就输出(此刻我们处于N+1行)
        return;             //返回到第N行
    }
    for (int j = N-1; j >=0; --j) //为啥J倒过来数呢?方便下面if中的偏移判断
    {
        if (!(((left|col|right)>>j)&1))//(自己解读,有助于提升能力 )
        {
            ans[i] = N-j;
            Dfs(i + 1,(left|(1<<j))<<1,(col|(1<<j)),(right|(1<<j))>>1);  //更新“占用信息”的工作就放在参数传递这里了
                                              //这样子的好处就是,回溯的时候不用做什么恢复处理,
                                              //因为这一层的东西基本没变动
        }
    }
}

int main()
{
    cin >> N;
    Dfs(1,0,0,0);
    cout << total;
    system("pause");
    return 0;
}

Summarize:

  Clever use of bit operations can achieve the icing on the cake (pretentious), but most people yawn when they see bit operations &|^<<>>.

  But it just so happens that there are some problems that can perfectly fit the special properties of binary and bit operations (not referring to this ==). When you encounter them, you will be amazed at the wonder of 0101010....  


postscript:

  I was still too young back then. In fact, since bit operations were used, why not use this wonderful property?

    x & (−x) can get the position of the lowest bit 1 in the binary representation of x;

    x & (x−1) can set the lowest 1 in the binary representation of x to 0.

Guess you like

Origin blog.csdn.net/HowToPause/article/details/127319451