Two way bfs -- dimension reduction attack on the rearrangement of the nine palaces

Posted by riversr54 on Fri, 18 Feb 2022 12:45:29 +0100

What is bidirectional bfs?

Ordinary bfs traverses downward from one vertex, while bidirectional bfs traverses downward and upward from the two endpoints in turn when the end point is known, until the number of layers overlaps with the previous traversal, the answer comes out.
It is clear that a two-way bfs needs to meet a necessary condition - the end point must be known.

Easy to understand picture

How to write bidirectional bfs?

For traditional bfs, we traverse the sequence directly through the queue. When a node in a layer reaches the end point, the traversal ends.
For bi-directional bfs, if the node traversed currently appears in the element of the previous traversal, the traversal ends. Therefore, we need an operation to judge whether it exists in a pile of elements, so we need to use set. When the traversal condition of bidirectional bfs is only related to a given data, it can be traversed by set.
When bfs is implemented entirely with set, the advantage is to find out whether it exists.
However, most problems can not be completely implemented by set, such as this nine house rearrangement, because it can not traverse through the data of the original string, but still needs'. " In this case, only queue traversal can be used, while set is used to record the traversal results of each upper layer (only string), pos in queue is used for traversal, and string is the existence to judge whether the end point is reached, so string can be entered into set. Of course, we should not forget to prevent turning back. We also need a visit set that stores the strings that have been judged each time.

  1. So how to implement a set that only records the last traversal of one layer of elements? Before each traversal of the queue, initialize a set of temp, which is used to record a layer of elements of this traversal, and then assign its value to visit at the end_ End, we just need to judge visit every time_ End whether there is a target element.
  2. How to realize the traversal in turn? This is very simple. Prepare two queues q1 and q2. q1 is used for traversal. After traversal of each layer, q1 and q2 can be traversed in turn by exchanging at the end.
  3. We must pay attention to prevent the processing of turning back, otherwise it will be infinite circulation. There are many ways to process this. Here, I process it one by one. In this way, there will be turning back nodes when joining the team, but it will be continue d when it needs to be processed.

Example: Jiugong rearranges bidirectional bfs code


The code looks very long, but it is actually a basic routine. The bfs template can be memorized in seconds.

#include<bits/stdc++.h>
using namespace std;
//Create a structure to store pos for operation
typedef struct{
    string s;
    int pos;
}grid;
int dx[4] = {-1,1,0,0};
int dy[4] = {0,0,-1,1};
int bfs(grid &start,grid &end){
    //Record the previous level of traversal in order to find the answer
    unordered_set<string>visit_end;
    //Record the whole traversal to prevent turning back
    unordered_set<string>visit;
    visit_end.insert(end.s);
    queue<grid>q1;
    queue<grid>q2;
    q1.push(start);
    q2.push(end);
    int step = 0;
    while (!q1.empty()&&!q2.empty()) {
        int size = q1.size();
        //This layer element is used to record this traversal
        unordered_set<string>temp;
        for (int i = 0; i < size; ++i) {
            grid cur = q1.front();
            q1.pop();
            if(visit_end.count(cur.s))
                return step;
            //Prevent turning back
            if(visit.count(cur.s))
                continue;
            visit.insert(cur.s);
            //Convert the number of one-dimensional description to two-dimensional description
            int x = cur.pos/3;
            int y = cur.pos - 3*x;
            for (int j = 0; j < 4; ++j) {
                int t_x=x;
                int t_y=y;
                t_x = t_x+dx[j];
                t_y = t_y+dy[j];
                if(t_x>=0&&t_x<3&&t_y>=0&&t_y<3){
                int pos2 = 3*t_x+t_y;
                string tmp = cur.s;
                //Get new information and join the team.
                swap(tmp[cur.pos],tmp[pos2]);
                if(!visit.count(tmp)){
                    grid sub={tmp,pos2};
                    q1.push(sub);
                    temp.insert(sub.s);
                }
                }
            }
        }
        step++;
        //Exchange processing and visit in turn_ End processing
        queue<grid>tt;
        tt = q1;
        q1 = q2;
        q2 = tt;
        visit_end = temp;
    }
    return -1;
}
int main(){
string s;
string t;
cin>>s>>t;
int pos1 = 0;
int pos2 = 0;
for(int i=0;i<s.size();i++){
    if(s[i]=='.')
        pos1 = i;
}
for (int j = 0; j < t.size(); ++j) {
        if(t[j]=='.')
            pos2 = j;
}
//Construct two structures written by yourself to update the answers
grid st = {s,pos1};
grid  end = {t,pos2};
int res = bfs(st,end);
cout<<res<<endl;
    return  0;
}

Comparison of the efficiency of one-way bfs and two-way bfs for this question:

This gap is really terrible.. (is this the same time complexity,,,,)

Share similar topics:

There is also a two-way bfs topic on leetcode. You can do it:
Unlocking times

Topics: Algorithm data structure queue