广度优先搜索解决“营救公主”问题

新浪微博 QQ空间

在之前的一篇文章(迷宫营救公主算法)中提供了一个半成品的解决方案,之所以说他是半成品,是因为首先选择的算法就不对,采用的是深度优先搜索,其次也没有真正的用对深度优先算法,走过的点应该标记为已经走过,而不应该重复遍历该节点。下面的文章对于广度优先和深度优先两种算法的解释非常到位。今天准备把这个问题再完整的用正确的算法解答一遍。

文章链接:【图论】广度优先搜索和深度优先搜索

就营救公主的问题而言,这里不再重复描述问题了,请点击这里查看题目分析:链接。这次选用广度优先算法来解决该问题。上面的算法介绍文章中对该算法已经分析得非常清晰了。先给解决本问题的算法伪代码:

这里每个节点有三种状态:

*)该状态表示墙,如果访问到该类型节点,则不用继续遍历。应退回至上层节点。

.)该状态表示该节点通行,访问到该节点应该将节点压入队列。并将该节点标记为已经访问。为区别图中已经有的几种元素,这里定义一个新的标记’#’来表示已经访问过的节点。同时可以使用一个二维数组记录从起始节点到该节点的访问距离step[i][j],该值应该是由其上一个节点的值加1之后得到。这样起始节点的值为1,依次每一个节点的距离都可以得到。

P)该状态表示已经搜索到了公主的位置,搜索结束。这时给出P点的距离,如果该距离值在给定的范围内,则可以营救出公主,否则失败。

解决问题的关键在于用一个step数组记录每个节点的距离起始点的距离,另外用一个’#’字符标记该节点已经被访问过,避免走“回头路”。

BFS(G, s)    
    init V[G]   // 由测试用例实现。
    status[s] = S    // s王子的位置,通过一次遍历可以获取其坐标。
    
    int count = -1;
    queue q
    q.add(s) // 将王子的位置入队。
    while !q.isEmpty()
        t = q.poll()
        for each vertex v in Adj[t] // 与t邻接的点
            if status[v] = 'P'    // 只对未访问的操作
                count = step[v] = step[t] + 1
                q.clear()
                break
            elseif status[v] = '.'    
                status[v] = '#'    // 标记为已经走过该节点
                q.add(v)
                step[v] = step[t] + 1 // 累加得到当前节点的步长
            elseif status[v] = '*'
                step[v] = -1   // 遇到墙,则将该节点的距离置为-1,在遍历完整个图时,
                               // 如果遇到最后一个节点的值为-1,则表明没有找到。
            else
                error
            
    if count != -1
        if count < L
            return true
    return false

 

有了上面的伪代码,翻译成代码也就非常简单了。
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;

/**
 * 公主被魔王抓走了,王子需要拯救出美丽的公主。他进入了魔王的城堡,魔王的城堡是一座很大的迷宫。
 * 为了使问题简单化,我们假设这个迷宫是一个N*M的二维方格。迷宫里有一些墙,王子不能通过。王子只
 * 能移动到相邻(上下左右四个方向)的方格内,并且一秒只能移动一步。地图由'S','P','.','*'
 * 四种符号构成,'.'表示王子可以通过,'*'表示墙,王子不能通过;'S'表示王子的位置;'P'表示公主
 * 的位置;T表示公主存活的剩余时间,王子必须在T秒内到达公主的位置,才能救活公主。如下图所示:
 */
public class SavePrincess
{
    private int m;
    private int n;
    private char[][] visited;
    private Position s = null;
    private Position p = null;

    public static void main(String[] args)
    {
        char maze[][] = {
        /* 0 1 2 3 4 5 6 7 8 9 */
        /* 0 */{ '.', '*', '.', '.', '.', '*', '.', '.', '.', '.' },
        /* 1 */{ '*', 'S', '*', '.', '.', '.', '.', '.', '.', '.' },
        /* 2 */{ '.', '*', '*', '.', '.', '.', '.', '.', '.', '.' },
        /* 3 */{ '.', '.', '*', '*', '*', '.', '.', '.', '.', '.' },
        /* 4 */{ '.', '.', '.', '.', '.', '.', '.', '.', '.', '.' },
        /* 5 */{ '.', '.', '.', '.', '*', '.', '.', '.', '.', '.' },
        /* 6 */{ '.', '.', '.', '.', '*', '.', '.', '.', '.', '.' },
        /* 7 */{ '.', '.', '.', '.', '*', '.', '.', '.', '.', '.' },
        /* 8 */{ '.', '.', '.', '.', '*', '.', '.', '.', '.', '.' },
        /* 9 */{ '.', 'P', '.', '.', '*', '.', '.', '.', '.', '.' } };

        new SavePrincess().saved(maze, 10, 8, 11);
    }

    /**
     * @param vis
     *            M行N列迷宫
     * @param m
     *            M
     * @param n
     *            N
     * @param t
     *            T
     * 
     * @return boolean true表示营救成功,反之表示失败。
     */
    public boolean saved(char[][] vis, int m, int n, int t)
    {
        if (t < 0)
        {
            return false;
        }

        this.m = m;
        this.n = n;
        this.visited = vis;
        int step[][] = new int[m][n];

        init(m, n, step);

        if (s == null || p == null)
        {
            System.out.println("input visited error!");
            return false;
        }

        Queue<Position> queue = new LinkedList<Position>();
        queue.add(s);
        int l = -1;
        while (!queue.isEmpty())
        {
            Position e = queue.poll();
            l = dealOneNode(step, queue, e);
        }

        if (l != -1 && l <= t)
        {
            System.out.println("Saved the princess in " + l + " seconds!");
            return true;
        }

        System.out.println("failed saved the princess! " + l + " > " + "t");
        return false;
    }

    private void init(int m, int n, int[][] step)
    {
        for (int i = 0; i != m; i++)
        {
            for (int j = 0; j != n; j++)
            {
                System.out.print(visited[i][j]);
                System.out.print(' ');
                step[i][j] = 0;
                switch (visited[i][j])
                {
                case '*':
                    break;
                case 'S':
                    this.s = new Position(i, j, 'S');
                    break;
                case '.':
                    break;
                case 'P':
                    this.p = new Position(i, j, 'P');
                    break;
                default:
                    System.out.println("input visited error!");
                    System.exit(-1);
                }
            }
            System.out.println("");
        }
    }

    private int dealOneNode(int[][] step, Queue<Position> queue, Position e)
    {
        /**
         * 在求相邻节点时做了一点优化,遇到墙的节点则不放到相邻节点中。
         * 从设计思想和可读性上看不是太好,从运行效率考虑,可以。
         */
        for (Position c : getNextPostions(e))
        {
            switch (c.getV())
            {
            case 'P':
                queue.clear();
                return step[e.getI()][e.getJ()] + 1;
            case '.':
                visited[c.getI()][c.getJ()] = '#';
                queue.add(c);
                step[c.getI()][c.getJ()] = step[e.getI()][e.getJ()] + 1;
                break;
            default:
                // 不可能进入该分支。
                System.err.println("some error!");
                break;
            }
        }
        return -1;
    }

    /**
     * 取有效节点。
     * 遇到墙则不加入相邻节点中。
     * 
     * @param e
     * @return
     */
    private List<Position> getNextPostions(Position e)
    {
        int i = e.getI();
        int j = e.getJ();
        List<Position> lst1 = new LinkedList<Position>();
        addOneNode(i + 1, j, lst1);
        addOneNode(i, j + 1, lst1);
        addOneNode(i - 1, j, lst1);
        addOneNode(i, j - 1, lst1);
        return lst1;
    }

    private void addOneNode(int i, int j, List<Position> lst)
    {
        if (i >= m || i < 0 || j >= n || j < 0)
        {
            return;
        }

        char v = visited[i][j];
        switch (v)
        {
        case '.':
        case 'P':
            lst.add(new Position(i, j, v));
            break;
        default:
            break;
        }
    }

    private class Position
    {
        private int i;
        private int j;
        private char v;

        public Position(int i, int j, char value)
        {
            this.i = i;
            this.j = j;
            this.v = value;
        }

        public char getV()
        {
            return v;
        }

        public int getI()
        {
            return i;
        }

        public int getJ()
        {
            return j;
        }

        public String toString()
        {
            return "(" + i + ", " + j + ")";
        }
    }

}

新浪微博 QQ空间

| 1 分2 分3 分4 分5 分 (5.00- 3票) Loading ... Loading ... | 这篇文章归档在:Java, 算法数据结构 | 标签: , , , , , . | 永久链接:链接 | 评论(6) |

6 条评论

  1. white_saber
    评论于 三月 31, 2014 at 20:47:58 CST | 评论链接

    char maze[][] = {
    /* 0 1 2 3 4 5 6 7 8 9 */
    /* 0 */{'.', '.', '.', '.', '.', '*', '.', '.', '.', '.'},
    /* 1 */{'.', 'P', '.', '.', '.', '.', '.', '.', '.', '.'},
    /* 2 */{'.', '*', '.', '.', '.', '.', '.', '.', '.', '.'},
    /* 3 */{'.', '.', '*', '*', '*', '.', '.', '.', '.', '.'},
    /* 4 */{'.', '.', '.', '.', '.', '.', '.', '.', '.', '.'},
    /* 5 */{'.', '.', '.', '.', '*', '.', '.', '.', '.', '.'},
    /* 6 */{'.', '.', '.', '.', '*', '.', '.', '.', '.', '.'},
    /* 7 */{'.', '.', '.', '.', '*', '.', '.', '.', '.', '.'},
    /* 8 */{'.', '.', '.', '.', '*', '.', '.', '.', '.', '.'},
    /* 9 */{'.', 'S', '.', '.', '*', '.', '.', '.', '.', '.'}}; 
    这个矩阵得出的结果不对,不是最优解

    然后解的路径没有。只有是否能在规定步数内救出公主的true?false,需要输出最优解的路径

    • 评论于 三月 31, 2014 at 21:57:34 CST | 评论链接

      根据这个迷宫,已经找到了代码中的bug,并修改了日志中代码。
      这位同学测试得好细心啊。赞并感谢!!

      你提出的记录路劲的需求,应该是要扩充每遍历一个节点时,不仅仅要记录该节点距离起始点“S”的距离,还需要记录起始节点“S”到当前节点的轨迹,这样就可以在找到公主时打印出路径了。因此setup不是一个简单的int型的二维数组,而应该是一个符合类型对象的二维数组,该对象中包含了距离和所经过的节点的链表。

      01char maze[][] = {
      02/* 0 1 2 3 4 5 6 7 8 9 */
      03/* 0 */{'.', '.', '.', '.', '.', '*', '.', '.', '.', '.'},
      04/* 1 */{'.', 'P', '.', '.', '.', '.', '.', '.', '.', '.'},
      05/* 2 */{'.', '*', '.', '.', '.', '.', '.', '.', '.', '.'},
      06/* 3 */{'.', '.', '*', '*', '*', '.', '.', '.', '.', '.'},
      07/* 4 */{'.', '.', '.', '.', '.', '.', '.', '.', '.', '.'},
      08/* 5 */{'.', '.', '.', '.', '*', '.', '.', '.', '.', '.'},
      09/* 6 */{'.', '.', '.', '.', '*', '.', '.', '.', '.', '.'},
      10/* 7 */{'.', '.', '.', '.', '*', '.', '.', '.', '.', '.'},
      11/* 8 */{'.', '.', '.', '.', '*', '.', '.', '.', '.', '.'},
      12/* 9 */{'.', 'S', '.', '.', '*', '.', '.', '.', '.', '.'}};

       

    • 评论于 三月 31, 2014 at 22:31:26 CST | 评论链接

      增加路径记录的代码如下:

      001import java.util.LinkedList;
      002import java.util.List;
      003import java.util.Queue;
      004 
      005/**
      006 * 公主被魔王抓走了,王子需要拯救出美丽的公主。他进入了魔王的城堡,魔王的城堡是一座很大的迷宫。
      007 * 为了使问题简单化,我们假设这个迷宫是一个N*M的二维方格。迷宫里有一些墙,王子不能通过。王子只
      008 * 能移动到相邻(上下左右四个方向)的方格内,并且一秒只能移动一步。地图由'S','P','.','*'
      009 * 四种符号构成,'.'表示王子可以通过,'*'表示墙,王子不能通过;'S'表示王子的位置;'P'表示公主
      010 * 的位置;T表示公主存活的剩余时间,王子必须在T秒内到达公主的位置,才能救活公主。如下图所示:
      011 */
      012public class SavePrincess
      013{
      014    private int m;
      015    private int n;
      016    private char[][] visited;
      017    private Position s = null;
      018    private Position p = null;
      019 
      020    public static void main(String[] args)
      021    {
      022        char maze[][] = {
      023        /* 0 1 2 3 4 5 6 7 8 9 */
      024        /* 0 */{ '.', '*', '.', '.', '.', '*', '.', '.', '.', '.' },
      025        /* 1 */{ '.', 'S', '.', '.', '.', '.', '.', '.', '.', '.' },
      026        /* 2 */{ '*', '*', '*', '.', '.', '.', '.', '.', '.', '.' },
      027        /* 3 */{ '.', '.', '*', '.', '*', '.', '.', '.', '.', '.' },
      028        /* 4 */{ '.', '.', '.', '.', '.', '.', '.', '.', '.', '.' },
      029        /* 5 */{ '.', '.', '.', '.', '*', '.', '.', '.', '.', '.' },
      030        /* 6 */{ '.', '.', '.', '.', '*', '.', '.', '.', '.', '.' },
      031        /* 7 */{ '.', '.', '.', '.', '*', '.', '.', '.', '.', '.' },
      032        /* 8 */{ '.', '.', '.', '.', '*', '.', '.', '.', '.', '.' },
      033        /* 9 */{ '.', 'P', '.', '.', '*', '.', '.', '.', '.', '.' } };
      034 
      035        new SavePrincess().saved(maze, 10, 8, 20);
      036    }
      037 
      038    /**
      039     * @param vis
      040     *            M行N列迷宫
      041     * @param m
      042     *            M
      043     * @param n
      044     *            N
      045     * @param t
      046     *            T
      047     *
      048     * @return boolean true表示营救成功,反之表示失败。
      049     */
      050    public boolean saved(char[][] vis, int m, int n, int t)
      051    {
      052        if (t < 0)
      053        {
      054            return false;
      055        }
      056 
      057        this.m = m;
      058        this.n = n;
      059        this.visited = vis;
      060        Step step[][] = new Step[m][n];
      061 
      062        init(m, n, step);
      063 
      064        if (s == null || p == null)
      065        {
      066            System.out.println("input visited error!");
      067            return false;
      068        }
      069 
      070        Queue<Position> queue = new LinkedList<Position>();
      071        queue.add(s);
      072        Step l = null;
      073        while (!queue.isEmpty())
      074        {
      075            Position e = queue.poll();
      076            l = dealOneNode(step, queue, e);
      077        }
      078 
      079        if (l != null && l.getStep() + 1 <= t)
      080        {
      081            System.out.println("Saved the princess in " + l + " seconds!");
      082            return true;
      083        }
      084 
      085        System.out.println("failed saved the princess! " + l + " > " + "t");
      086        return false;
      087    }
      088 
      089    private void init(int m, int n, Step[][] step)
      090    {
      091        for (int i = 0; i != m; i++)
      092        {
      093            for (int j = 0; j != n; j++)
      094            {
      095                System.out.print(visited[i][j]);
      096                System.out.print(' ');
      097                step[i][j] = new Step(0);
      098                switch (visited[i][j])
      099                {
      100                case '*':
      101                    break;
      102                case 'S':
      103                    this.s = new Position(i, j, 'S');
      104                    break;
      105                case '.':
      106                    break;
      107                case 'P':
      108                    this.p = new Position(i, j, 'P');
      109                    break;
      110                default:
      111                    System.out.println("input visited error!");
      112                    System.exit(-1);
      113                }
      114            }
      115            System.out.println("");
      116        }
      117    }
      118 
      119    private Step dealOneNode(Step[][] step, Queue<Position> queue, Position e)
      120    {
      121        /**
      122         * 在求相邻节点时做了一点优化,遇到墙的节点则不放到相邻节点中。
      123         * 从设计思想和可读性上看不是太好,从运行效率考虑,可以。
      124         */
      125        for (Position c : getNextPostions(e))
      126        {
      127            switch (c.getV())
      128            {
      129            case 'P':
      130                queue.clear();
      131                return step[e.getI()][e.getJ()];
      132            case '.':
      133                visited[c.getI()][c.getJ()] = '#';
      134                queue.add(c);
      135                step[c.getI()][c.getJ()].setStep(step[e.getI()][e.getJ()].step + 1);
      136                List<Position> l = step[c.getI()][c.getJ()].getPath();
      137                l.addAll(step[e.getI()][e.getJ()].path);
      138                l.add(c);
      139                break;
      140            default:
      141                // 不可能进入该分支。
      142                System.err.println("some error!");
      143                break;
      144            }
      145        }
      146        return null;
      147    }
      148 
      149    /**
      150     * 取有效节点。
      151     * 遇到墙则不加入相邻节点中。
      152     *
      153     * @param e
      154     * @return
      155     */
      156    private List<Position> getNextPostions(Position e)
      157    {
      158        int i = e.getI();
      159        int j = e.getJ();
      160        List<Position> lst1 = new LinkedList<Position>();
      161        addOneNode(i + 1, j, lst1);
      162        addOneNode(i, j + 1, lst1);
      163        addOneNode(i - 1, j, lst1);
      164        addOneNode(i, j - 1, lst1);
      165        return lst1;
      166    }
      167 
      168    private void addOneNode(int i, int j, List<Position> lst)
      169    {
      170        if (i >= m || i < 0 || j >= n || j < 0)
      171        {
      172            return;
      173        }
      174 
      175        char v = visited[i][j];
      176        switch (v)
      177        {
      178        case '.':
      179        case 'P':
      180            lst.add(new Position(i, j, v));
      181            break;
      182        default:
      183            break;
      184        }
      185    }
      186 
      187    private class Position
      188    {
      189        private int i;
      190        private int j;
      191        private char v;
      192 
      193        public Position(int i, int j, char value)
      194        {
      195            this.i = i;
      196            this.j = j;
      197            this.v = value;
      198        }
      199 
      200        public char getV()
      201        {
      202            return v;
      203        }
      204 
      205        public int getI()
      206        {
      207            return i;
      208        }
      209 
      210        public int getJ()
      211        {
      212            return j;
      213        }
      214 
      215        public String toString()
      216        {
      217            return "[" + i + ", " + j + "]";
      218        }
      219    }
      220     
      221    private class Step
      222    {
      223        private int step;
      224         
      225        private List<Position> path = new LinkedList<Position>();
      226         
      227        public Step(int step)
      228        {
      229            this.step = step;
      230        }
      231         
      232        public int getStep()
      233        {
      234            return step;
      235        }
      236         
      237        public void setStep(int step)
      238        {
      239            this.step = step;
      240        }
      241         
      242        public List<Position> getPath()
      243        {
      244            return path;
      245        }
      246         
      247        @Override
      248        public String toString()
      249        {
      250            StringBuilder sb = new StringBuilder();
      251            sb.append("(").append("step: ").append(step).append(", path: ");
      252            if (path != null)
      253            {
      254                for (Position pnode : path)
      255                {
      256                    sb.append(pnode).append(" -> ");
      257                }
      258            }
      259            sb.append(p).append(")");
      260            return sb.toString();
      261        }
      262    }
      263 
      264}

       

      运行结果:
      . * . . . * . .
      . S . . . . . .
      * * * . . . . .
      . . * . * . . .
      . . . . . . . .
      . . . . * . . .
      . . . . * . . .
      . . . . * . . .
      . . . . * . . .
      . P . . * . . .
      Saved the princess in (step: 11, path: [1, 2] -> [1, 3] -> [2, 3] -> [3, 3] -> [4, 3] -> [5, 3] -> [6, 3] -> [7, 3] -> [8, 3] -> [9, 3] -> [9, 2] -> [9, 1]) seconds!

  2. 评论于 十月 17, 2013 at 13:32:25 CST | 评论链接

                                   

  3. 评论于 十月 15, 2013 at 15:34:07 CST | 评论链接

    对于网站降权方面,我能博主一些问题么。我的一个网站www.caizicheng.com六月份改版之后,被降权了。不过在7月份有一段时间,权重回来之后现在又被降权了。而且,这次网站的首页也找不到了。
    现在,我应该从那些方面着手来恢复网站的权重。 

    • 评论于 十月 15, 2013 at 17:52:53 CST | 评论链接

      在这方面,你比我懂得多多了。我只觉得一个稳固而实用的界面和充实的内容非常重要。

评论

邮箱地址不会被泄露, 标记为 * 的项目必填。

8 - 2 = *



You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <img alt="" src="" class=""> <pre class=""> <q cite=""> <s> <strike> <strong>