【双向BFS】hdu 1401 Solitaire

题目描述:

http://acm.hdu.edu.cn/showproblem.php?pid=1401

 

中文大意:

二维棋盘上,给定了 4 个棋子的初始状态和目标状态。

问 8 步内能否实现从初始状态到目标状态的变化。

已知棋盘大小为 8*8,每个棋子可以上下左右移动。

若相邻位置已有棋子,则可以实现跳棋。

 

思路:

采用双向 BFS 算法,同时对初始状态和目标状态进行广搜。

故需要两个队列 q1 q2,用来分别记录两个搜索过程的节点信息。

正向搜索过程:

队列 q1 首节点为当前棋盘状态。

如果当前状态的代价已经 >= 4,则正向搜索过程结束,执行逆向搜索。

如果当前状态已经被逆向搜索过,则说明 8 步内可到达,过程结束。 

下一状态有 16 种选择:移动任意一个棋子,在一个方向上任意移动。

需要判断棋子的新位置是否超出棋盘范围,是否可以跳棋,跳棋之后是否又超出棋盘范围。

最终,在确定了棋子的新位置后,需要检查该状态是否被访问过。

如果未被访问过,则将新状态做正向搜索标记,并放入到队列 q1 中。

如果被访问过,且有逆向搜索标记,则说明 8 步内可到达,过程结束。

注意:在判断某一状态是否被访问过时,需要将棋子的位置排序。

否则,visited[p1][p2][p3][p4] 和 visited[p2][p1][p3][p4] 同一状态会被标记两次。

逆向搜索过程与正向搜索过程类似。

 

代码:

#include<bits/stdc++.h>
using namespace std;

struct point{
    int x,y;
    
    //是否超出边界 
    bool check(){
        return x>=0&&x<8&&y>=0&&y<8;
    }
};

struct node{
    point p[4]; 
    int step;
}st,ed;//开始状态,目标状态 

//将四个点的位置按从小到大的顺序排序
//避免相同情况重复统计 
bool cmp(point a, point b){
    int p1 = a.y * 8 + a.x;
    int p2 = b.y * 8 + b.x;
    
    return p1 < p2;
}
 
char visited[64][64][64][64] = {'0'};

//将当前状态标记为"已被搜索" 
void set_visited(node n, char c){
    int p1 = n.p[0].y * 8 + n.p[0].x;
    int p2 = n.p[1].y * 8 + n.p[1].x;
    int p3 = n.p[2].y * 8 + n.p[2].x;
    int p4 = n.p[3].y * 8 + n.p[3].x;
    
    visited[p1][p2][p3][p4] = c;
}

//获取当前状态的标记信息 
char get_visited(node n){
    int p1 = n.p[0].y * 8 + n.p[0].x;
    int p2 = n.p[1].y * 8 + n.p[1].x;
    int p3 = n.p[2].y * 8 + n.p[2].x;
    int p4 = n.p[3].y * 8 + n.p[3].x;
    
    return visited[p1][p2][p3][p4];
}

int dir[4][2] = {{0,-1}, {0,1}, {-1,0}, {1,0}};

//四个点的位置 
int p_map[8][8];

//标记四个点的位置信息 
void set_map(node n){
    memset(p_map, 0, sizeof(p_map));
    
    p_map[n.p[0].y][n.p[0].x] = 1;
    p_map[n.p[1].y][n.p[1].x] = 1;
    p_map[n.p[2].y][n.p[2].x] = 1;
    p_map[n.p[3].y][n.p[3].x] = 1;
}

//判断是否跳棋 
bool jump(point p){
    //若目标位置已存在棋子,则跳棋 
    if(p_map[p.y][p.x] == 1){
        return true;
    }
    return false;
}

bool bfs(){
    memset(visited, '0', sizeof(visited));
    
    //双向BFS,故某一状态存在两种情况
    //正向被搜索到 
    //逆向被搜索到 
    set_visited(st, '1');
    set_visited(ed, '2');
    
    st.step = 0;
    ed.step = 0;
    
    //声明两个队列,分别记录节点信息 
    queue<node> q1,q2;
    q1.push(st);
    q2.push(ed);
    
    node next;
    while(!q1.empty() || !q2.empty()){
        //正向搜索 
        if(!q1.empty()){
            st = q1.front();
            q1.pop();
            
            //已经正向搜索了四步 
            if(st.step >= 4){
                continue;
            }
            
            //当前状态已经被反向搜索到 
            if(get_visited(st) == '2'){
                return true;
            }
            
            //标记四点位置信息 
            set_map(st);
            
            //尝试移动四个点中的一个 
            for(int i=0;i<4;i++){
                //尝试四个方向 
                for(int j=0;j<4;j++){
                    next = st;
                    next.p[i].x += dir[j][0];
                    next.p[i].y += dir[j][1];
                    next.step++;
                    
                    //超出范围 
                    if(!next.p[i].check()){
                        continue;
                    }
                    
                    //跳棋 
                    if(jump(next.p[i])){
                        next.p[i].x += dir[j][0];
                        next.p[i].y += dir[j][1];
                        
                        //超出范围 
                        if(!next.p[i].check()){
                            continue;
                        }
                    }
                    
                    //将节点按位置信息排序,避免重复统计 
                    sort(next.p, next.p+4, cmp);
                    
                    char flag = get_visited(next);
                    //当前状态没有被搜索到过 
                    if(flag == '0'){
                        //做正向搜索标记 
                        set_visited(next, '1');                        
                        q1.push(next);
                    }
                    else if(flag == '2'){//当前状态被反向搜索到过 
                        return true;
                    }    
                }
            }
        }
        
        //反向搜素,同理 
        if(!q2.empty()){
            ed = q2.front();
            q2.pop();
            
            if(ed.step >= 4){
                continue;
            }
            
            if(get_visited(ed) == '1'){
                return true;
            }
            
            set_map(ed);
            
            for(int i=0;i<4;i++){
                for(int j=0;j<4;j++){
                    next = ed;
                    next.p[i].x = ed.p[i].x + dir[j][0];
                    next.p[i].y = ed.p[i].y + dir[j][1];
                    next.step++;
                    
                    if(!next.p[i].check()){
                        continue;
                    }
                    
                    if(jump(next.p[i])){
                        next.p[i].x += dir[j][0];
                        next.p[i].y += dir[j][1];
                        
                        if(!next.p[i].check()){
                            continue;
                        }
                    }
                    
                    sort(next.p, next.p+4, cmp);
                    
                    char flag = get_visited(next);
                    if(flag == '0'){
                        set_visited(next, '2');
                        q2.push(next);
                    }
                    else if(flag == '1'){
                        return true;
                    }    
                }
            }
        }
    }
    //8步内不能到达 
    return false;    
}

int main(){
    while(~scanf("%d%d", &st.p[0].y, &st.p[0].x)){
        for(int i=1;i<4;i++){
            scanf("%d%d", &st.p[i].y, &st.p[i].x);
        }
        for(int i=0;i<4;i++){
            scanf("%d%d", &ed.p[i].y, &ed.p[i].x);
        }
        
        for(int i=0;i<4;i++){
            st.p[i].x--;
            st.p[i].y--;
            ed.p[i].x--;
            ed.p[i].y--;
        }
        
        sort(st.p, st.p+4, cmp);
        sort(ed.p, ed.p+4, cmp);
        
        bool result = bfs();
        
        if(result){
            printf("YES\n");
        }
        else{
            printf("NO\n");
        }
    }
} 

 

posted @ 2021-02-15 22:52  狂奔的小学生  阅读(189)  评论(0编辑  收藏  举报