我研究了使用Dijkstra算法(由Weather Vane建议),这将要求为每个网格单元存储到起点的距离和从前一个单元格的方向。不幸的是,在32x16的网格上,路径的距离可能大于255;我发现的最长路径距离为319(见下面的左图)。这意味着距离无法适应8位,距离矩阵的大小为1024字节。
![longest path and largest queue](https://istack.dev59.com/6bFtc.webp)
左:最长路径(距离=319)。右:等距单元格的最大数量(16个单位距离处的72个单元格)
然而,在所有距离相等的正方形网格中,可以将Dijkstra简化为不使用距离矩阵的广度优先搜索;如果使用FIFO队列,则按照到起始单元格的距离顺序访问单元格,因此无法找到已访问单元格的更短路径。
FIFO队列将包含特定距离的每个单元格,然后逐渐过渡到距离+1等。队列的最大大小取决于可能存在的等距单元格数量;我发现最大值为72(请参见上图右侧),在从以前的距离过渡时,这需要一个可以容纳76个单元格坐标或152字节的队列。
算法返回的路径是一个数组,包含最多320个单元格的坐标,因此其最大尺寸为640字节。在构建此数组之前,可以丢弃队列,因此仅在内存中同时保存方向网格和路径。
以下是简化算法的代码示例,仅包含方向矩阵和FIFO队列;它可能在许多方面得到改进,但它展示了这个想法。findPath()函数使用最少664字节至最多1152字节的分配内存(取决于路径长度),再加上约20字节的附加变量。
可以进一步减少,例如将方向矩阵存储为4位半字节,将其大小从512字节减小到256字节(但需要更多计算),或者将路径作为上/右/下/左方向的序列返回而不是单元格坐标,每步只需要2位,将其最大大小从640字节减小到80字节。
#include <stdlib.h>
short int findPath(char grid[][32], char x1, char y1, char x2, char y2, char **path) {
char (*dir)[16][32] = calloc(512, 1);
(*dir)[y2][x2] = 5;
char *queue = malloc(152);
queue[0] = x2; queue[1] = y2;
unsigned char qRead = 0, qWrite = 2;
char qCurSize = 1, qNextSize = 0;
short int distance = 0;
char dx[4] = {0, 1, 0, -1};
while (qRead != qWrite && !(*dir)[y1][x1]) {
char x = queue[qRead++], y = queue[qRead++];
qRead %= 152;
for (char i = 0; i < 4; i++) {
char nx = x + dx[i], ny = y + dx[3 - i];
if (nx >= 0 && nx < 32 && ny >= 0 && ny < 16
&& !grid[ny][nx] && !(*dir)[ny][nx]) {
(*dir)[ny][nx] = i + 1;
queue[qWrite++] = nx; queue[qWrite++] = ny;
qWrite %= 152;
++qNextSize;
}
}
if (!--qCurSize || (*dir)[y1][x1]) {
qCurSize = qNextSize;
qNextSize = 0;
++distance;
}
}
free(queue);
if (!(*dir)[y1][x1]) distance = -1;
else {
*path = malloc(distance * 2 + 2);
(*path)[0] = x1; (*path)[1] = y1;
for (short int i = 1; i <= distance; i++) {
char d = (*dir)[y1][x1] - 1;
x1 -= dx[d]; y1 -= dx[3 - d];
(*path)[i * 2] = x1; (*path)[i * 2 + 1] = y1;
}
}
free(*dir);
return distance + 1;
}
int main() {
char grid[][32] =
{{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}};
char x1 = 31, y1 = 0, x2 = 16, y2 = 7, *path = NULL;
short int steps = findPath(grid, x1, y1, x2, y2, &path);
free(path);
return 0;
}