• 【双向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
    ");
            }
            else{
                printf("NO
    ");
            }
        }
    } 

     

    作者:老干妈就泡面
    本文版权归作者和博客园共有,欢迎转载,但请给出原文链接,并保留此段声明,否则保留追究法律责任的权利。
  • 相关阅读:
    字符串里输出字符c的所有位置
    python时间戳
    python之set()和issubset()方法
    python之判断键是否存在于字典中
    python之方法与函数的区别,及其传参
    接口测试之requests
    python之isinstance()函数
    MySQL创建表时,被``和''坑了很久
    游标位置self.cur.scroll(0, mode='absolute')
    python操作MySQL数据库
  • 原文地址:https://www.cnblogs.com/bjxqmy/p/14405702.html
Copyright © 2020-2023  润新知