迷宫问题
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 }