zoukankan      html  css  js  c++  java
  • Day2-J-逃离迷宫-HDU-1728

    给定一个m × n (m行, n列)的迷宫,迷宫中有两个位置,gloria想从迷宫的一个位置走到另外一个位置,当然迷宫中有些地方是空地,gloria可以穿越,有些地方是障碍,她必须绕行,从迷宫的一个位置,只能走到与它相邻的4个位置中,当然在行走过程中,gloria不能走到迷宫外面去。令人头痛的是,gloria是个没什么方向感的人,因此,她在行走过程中,不能转太多弯了,否则她会晕倒的。我们假定给定的两个位置都是空地,初始时,gloria所面向的方向未定,她可以选择4个方向的任何一个出发,而不算成一次转弯。gloria能从一个位置走到另外一个位置吗?

    Input  第1行为一个整数t (1 ≤ t ≤ 100),表示测试数据的个数,接下来为t组测试数据,每组测试数据中, 
      第1行为两个整数m, n (1 ≤ m, n ≤ 100),分别表示迷宫的行数和列数,接下来m行,每行包括n个字符,其中字符'.'表示该位置为空地,字符'*'表示该位置为障碍,输入数据中只有这两种字符,每组测试数据的最后一行为5个整数k, x 1, y 1, x 2, y 2 (1 ≤ k ≤ 10, 1 ≤ x1, x 2 ≤ n, 1 ≤ y 1, y 2 ≤ m),其中k表示gloria最多能转的弯数,(x 1, y 1), (x 2, y2)表示两个位置,其中x 1,x 2对应列,y 1, y 2对应行。 
    Output  每组测试数据对应为一行,若gloria能从一个位置走到另外一个位置,输出“yes”,否则输出“no”。Sample Input

    2
    5 5
    ...**
    *.**.
    .....
    .....
    *....
    1 1 1 1 3
    5 5
    ...**
    *.**.
    .....
    .....
    *....
    2 1 1 1 3

    Sample Output

    no
    yes

    分析:简单的BFS搜索最短路径,加上一个转弯的限制,就在判重的时候多加一个条件即可,但是直接判断是否访问过会造成漏解,剪枝条件应为:在该点的朝向的剩余转向次数
    是否大于等于该点已经访问(未访问)过的(等于是为了防止0转向次数被剪掉),相当于转弯次数为优先级,越多其对解的贡献越大。
    代码如下:
    const int maxm = 150;
    const int dx[] = {-1, 1, 0, 0};
    const int dy[] = {0, 0, -1, 1};
    
    struct Node {
        int x, y, turn, dire;
        Node(int _x = 0, int _y = 0, int _turn = -1,int _dire = -1):x(_x), y(_y),turn(_turn),dire(_dire) {}
    };
    
    int T, m, n, sx, sy, ex, ey, k, d[maxm][maxm][5];
    string buf[maxm];
    
    bool inside(int x,int y) {
        return x >= 0 && x < m && y >= 0 && y < n;
    }
    
    bool bfs() {
        memset(d, -1, sizeof(d));
        queue<Node> q;
        for (int i = 0; i < 4; ++i) {
            q.push(Node(sx, sy, k, i));
            d[sx][sy][i] = k;
        }
        while (!q.empty()){
            Node p = q.front();
            q.pop();
            if(p.x == ex && p.y == ey)
                return true;
            for (int i = 0; i < 4; ++i) {
                Node tmp = p;
                tmp.x += dx[i], tmp.y += dy[i];
                if(i == p.dire) {
                    if(inside(tmp.x, tmp.y) && buf[tmp.x][tmp.y] == '.' && d[tmp.x][tmp.y][i] <= p.turn) {
                        d[tmp.x][tmp.y][i] = p.turn;
                        q.push(tmp);
                    }
                } else if (tmp.turn > 0) {
                    tmp.turn--, tmp.dire = i;
                    if(inside(tmp.x, tmp.y) && buf[tmp.x][tmp.y] == '.' && d[tmp.x][tmp.y][i] <= p.turn) {
                        d[tmp.x][tmp.y][i] = p.turn;
                        q.push(tmp);
                    }
                }
            }
        }
        return false;
    }
    
    int main() {
        scanf("%d", &T);
        while(T--) {
            scanf("%d%d", &m, &n);
            for (int i = 0; i < m; ++i)
                cin >> buf[i];
            scanf("%d%d%d%d%d", &k, &sy, &sx, &ey, &ex);
            sy--, sx--, ey--, ex--;
            if(bfs())
                printf("yes
    ");
            else
                printf("no
    ");
        }
        return 0;
    }
    View Code
    
    

     虽然我们用转向次数大于等于的判断条件来使0转向次数不被剪掉,但也加入了许多重复的点,我们可以继续优化代码,既然转向后次数减少,那么对解的贡献也减小,到达相同的点相同的方向的优先级一定小于不转弯的,那么,我们可以做出如下策略:如果不转弯,直接将前进后的入队,如果转弯,只转弯不前进入队,这样,转弯的情况就比直走慢了一步,自然就会有优先级上的差别,直接上代码进行体会:

    const int maxm = 150;
    const int dx[] = {-1, 1, 0, 0};
    const int dy[] = {0, 0, -1, 1};
    
    struct Node {
        int x, y, turn, dire;
        Node(int _x = 0, int _y = 0, int _turn = -1,int _dire = -1):x(_x), y(_y),turn(_turn),dire(_dire) {}
    };
    
    int T, m, n, sx, sy, ex, ey, k, d[maxm][maxm][5];
    string buf[maxm];
    
    bool inside(int x,int y) {
        return x >= 0 && x < m && y >= 0 && y < n;
    }
    
    bool bfs() {
        memset(d, 0, sizeof(d));
        queue<Node> q;
        for (int i = 0; i < 4; ++i) {
            q.push(Node(sx, sy, k, i));
            d[sx][sy][i] = k;
        }
        while (!q.empty()){
            Node p = q.front();
            q.pop();
            if(p.x == ex && p.y == ey)
                return true;
            for (int i = 0; i < 4; ++i) {
                Node tmp = p;
                if(i == p.dire) {
                    tmp.x += dx[i], tmp.y += dy[i];
                    if(inside(tmp.x, tmp.y) && buf[tmp.x][tmp.y] == '.' && !d[tmp.x][tmp.y][i]) {
                        d[tmp.x][tmp.y][i] = 1;
                        q.push(tmp);
                    }
                } else if (tmp.turn > 0) {
                    tmp.turn--, tmp.dire = i;
                    if(!d[tmp.x][tmp.y][i]) {
                        d[tmp.x][tmp.y][i] = 1;
                        q.push(tmp);
                    }
                }
            }
        }
        return false;
    }
    
    int main() {
        scanf("%d", &T);
        while(T--) {
            scanf("%d%d", &m, &n);
            for (int i = 0; i < m; ++i)
                cin >> buf[i];
            scanf("%d%d%d%d%d", &k, &sy, &sx, &ey, &ex);
            sy--, sx--, ey--, ex--;
            if(bfs())
                printf("yes
    ");
            else
                printf("no
    ");
        }
        return 0;
    }
    View Code

    从110ms降到了60ms,代码上只进行了基础微调。

     
  • 相关阅读:
    简单明了的带你理解springboot原理和三大核心注解
    Spring Boot(一):入门篇
    【Mysql优化】聚簇索引与非聚簇索引概念
    Mysql索引原理与优化
    Mysql全文索引的使用
    索引的优缺点,如何创建索引
    184 01 Android 零基础入门 03 Java常用工具类03 Java字符串 02 String类 04 例:字符串与byte(即:字节)数组间的相互转换
    183 01 Android 零基础入门 03 Java常用工具类03 Java字符串 02 String类 03 String常用方法(下)
    182 01 Android 零基础入门 03 Java常用工具类03 Java字符串 02 String类 02 String常用方法(上)
    181 01 Android 零基础入门 03 Java常用工具类03 Java字符串 02 String类 01 String常用方法简介
  • 原文地址:https://www.cnblogs.com/GRedComeT/p/11229286.html
Copyright © 2011-2022 走看看