POJ 3984 迷宫问题 (Dijkstra)

迷宫问题

Description
定义一个二维数组:
int maze[5][5] = {
0, 1, 0, 0, 0,
0, 1, 0, 1, 0,
0, 0, 0, 0, 0,
0, 1, 1, 1, 0,
0, 0, 0, 1, 0,
};
它表示一个迷宫,其中的1表示墙壁,0表示可以走的路,只能横着走或竖着走,不能斜着走,要求编程序找出从左上角到
右下角的最短路线。

Input
一个5 × 5的二维数组,表示一个迷宫。数据保证有唯一解。

Output
左上角到右下角的最短路径,格式如样例所示。

Sample Input
0 1 0 0 0
0 1 0 1 0
0 0 0 0 0
0 1 1 1 0
0 0 0 1 0

Sample Output
(0, 0)
(1, 0)
(2, 0)
(2, 1)
(2, 2)
(2, 3)
(2, 4)
(3, 4)
(4, 4)

 

解决方案:

对于这题,我们可以把它maze数组转化为一个图,不能通过的话,即两点之间无路径,然后利用Dijkstra算法寻找S点到T点的最短路径。

Dijkstra算法可参考维基百科迪科斯彻算法,其核心代码如下:

 1 function Dijkstra(G, w, s)
 2      for each vertex v in V[G]                        // 初始化
 3            d[v] := infinity                           // 将各点的已知最短距离先设置成无穷大
 4            previous[v] := undefined                   // 各点的已知最短路径上的前趋都未知
 5      d[s] := 0                                        // 因为出发点到出发点间不需移动任何距离,所以可以直接将s到s的最小距离设为0
 6      S := empty set
 7      Q := set of all vertices
 8      while Q is not an empty set                      // Dijkstra演算法主體
 9            u := Extract_Min(Q)
10            S.append(u)
11            for each edge outgoing from u as (u,v)
12                   if d[v] > d[u] + w(u,v)             // 拓展边(u,v)。w(u,v)为从u到v的路径长度。
13                         d[v] := d[u] + w(u,v)         // 更新路径长度到更小的那个和值。
14                         previous[v] := u              // 记录前趋顶点

对于上图,我们的算法可以大大简化,因为每条路径的权重都相等,由于Dijkstra是按照广度优先搜索来进行遍历的,因而在我们这,先到达这个点的路径即为最短路径(这里对照上图,在纸上画一画就可以体会到)。

对于第9行的 u := Extract_Min(Q) ,我们就用一个队列来表示Q,因为先进来的点的距离不会大于后进来的点,每次从队列头部取出元素,效果与Extract_Min(Q)一样。

代码如下:

POJ 3984
 1 #include <stdio.h>
 2 #include <string.h>
 3 
 4 #define N 5
 5 
 6 void print(int value, int prev[N*N])
 7 {
 8     if (value >= 0)
 9     {
10         int x = value/N;
11         int y = value%N;
12         print(prev[value], prev);
13         printf("(%d, %d)\n", x, y);
14     }
15 }
16 
17 void solve(int maze[N][N])
18 {
19     int visited[N][N];//是否被访问了
20     memset(visited, 0, sizeof(visited));//初始化为0
21 
22     int offset[N-1][2] = {
23         {1, 0},//向上
24         {-1, 0},//向下
25         {0, -1},//向左
26         {0, 1}//向右
27     };
28 
29     int queue[N*N];//队列
30     int front = 0;//头指针
31     int rear = 0;//尾指针
32 
33     int x = 0;//起点x坐标
34     int y = 0;//起点y坐标
35     queue[++rear] = 0;//向队列添加第一个点,即起始点
36     visited[x][y] = 1;//0结点被访问过
37 
38     int prev[N*N];//保留每个点的前结点
39     memset(prev, -1, sizeof(prev));//初始化为-1
40     prev[0] = -1;//0的前结点为-1
41 
42     while (front <= rear)
43     {
44         int i;
45         int newx;
46         int newy;
47         front++;//相当于弹出了一个结点
48         x = queue[front]/N;
49         y = queue[front]%N;
50 
51         for (i = 0; i < N - 1; i++)
52         {
53             newx = x + offset[i][0];//偏移之后的x坐标
54             newy = y + offset[i][1];//偏移之后的y坐标
55             if (newx >= 0 && newx < N && newy >= 0 && newy < N && !visited[newx][newy] && !maze[newx][newy])
56             {
57                 rear = (rear + 1)%(N*N);
58                 queue[rear] = newx*N + newy;
59                 prev[newx*N + newy] = x*N + y;
60                 visited[newx][newy] = 1;
61             }
62         }
63     }
64     print(N*N - 1, prev);
65 }
66 
67 int main()
68 {
69     int maze[N][N];
70     int i = 0;
71     int j = 0;
72     while (scanf("%d", &maze[i][j++]) != EOF)
73     {
74         if (j == 5)
75         {
76             i++;
77             j = 0;
78         }
79         if (i == 5)
80         {
81             solve(maze);
82             i = 0;
83         }
84     }
85     return 0;
86 }

posted on 2012-06-22 13:57  NULL00  阅读(6518)  评论(1编辑  收藏  举报

导航