作者:小迅
链接:https://leetcode.cn/problems/shortest-path-to-get-all-keys/solutions/1960590/yan-du-you-xian-sou-suo-zhu-shi-chao-ji-75r4k/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
正常的广度优先遍历只需要使用一个数组充当队列,并且用入参获取迭代结果就可以了,但是这个题需要记录的东西太多了。思路:
-
- // 位运算宏,包括获取某一位的值和设置某一位的值
- #define GET_BIT(x, bit) (((x) & (1 << (bit))) >> (bit))
- #define SET_BIT(x, bit) ((x) |= (1 << (bit)))
- // 用于快速获取上下左右坐标
- int g_index[][2] = {{-1, 0}, {0, 1}, {1, 0}, {0, -1}};
- // 用于统计钥匙数量,获取开始节点坐标,同时在队列节点里要记录当前找到的钥匙状态和路径长度
- int *getKeyNumAndStart(char ** grid, int gridSize, int colSize, int *keyNum)
- {
- int *startIndex = (int *)malloc(sizeof(int) * 4);
- for (int i = 0; i < gridSize; i++) {
- for (int j = 0 ; j < colSize; j++) {
- if (grid[i][j] >= 'a' && grid[i][j] <= 'f') {
- (*keyNum)++;
- }
- if (grid[i][j] == '@') {
- startIndex[0] = i; // 开始节点坐标
- startIndex[1] = j;
- startIndex[2] = 0; // 钥匙状态,都没找到就是0
- startIndex[3] = 0; // 路径,一开始也是0
- }
- }
- }
- return startIndex;
- }
- // 获取当前已经找到的钥匙数量,需要统计低六位的1的数量
- int getCurKeyNum(int nodeKey)
- {
- int keyNum = 0;
- for (int i = 0; i < 6; i++) {
- int num = GET_BIT(nodeKey, i);
- keyNum += num;
- }
- return keyNum;
- }
- // 广度优先遍历
- void BFS(char ** grid, int **arrList, int start, int end, int keyNum, int *minPath, int ***visList,
- int gridSize, int colSize)
- {
- int startTemp = start;
- int endTemp = end;
- // 如果发现没有新的节点入队,即队列为空了,就退出返回-1
- if (start >= end) {
- return;
- }
- // 对该层节点进行遍历
- for (int i = startTemp; i < endTemp; i++) {
- // 获取一个节点
- int *tempNode = arrList[i];
- // 如果发现已经找到了所有钥匙,直接返回距离即可
- int curKeyNum = getCurKeyNum(tempNode[2]);
- if (curKeyNum == keyNum) {
- *minPath = tempNode[3];
- return;
- }
- // 否则继续
- for (int j = 0; j < 4 ; j++) {
- int x_temp = tempNode[0] + g_index[j][0];
- int y_temp = tempNode[1] + g_index[j][1];
- // 如果当前的节点坐标存在越界或者已经遍历过了,就跳过
- if (x_temp < 0 || y_temp < 0 || x_temp >= gridSize || y_temp >= colSize ||
- visList[x_temp][y_temp][tempNode[2]] == 1) {
- continue;
- }
- // 遇到 ‘#’ 就标记遍历过就行了,实际不做操作就行,标记其实应该都没必要
- if (grid[x_temp][y_temp] == '#') {
- visList[x_temp][y_temp][tempNode[2]] = 1;
- }
- // 遇到 “.” 或者“@”时(注意:有可能是反向跑回到起始点的,比如有的钥匙可能要绕一圈)
- // 将节点入队,并标记标记数组中的对应位置
- if (grid[x_temp][y_temp] == '.' || grid[x_temp][y_temp] == '@') {
- visList[x_temp][y_temp][tempNode[2]] = 1;
- arrList[end] = (int *)malloc(sizeof(int) * 4);
- arrList[end][0] = x_temp;
- arrList[end][1] = y_temp;
- arrList[end][2] = tempNode[2];
- arrList[end][3] = tempNode[3] + 1;
- end++;
- }
- // 遇到钥匙时,还是要标记一下当前状态的标记数组对应位置,但是状态马上就会改变;更新状态后将节点入队
- if (grid[x_temp][y_temp] >= 'a' && grid[x_temp][y_temp] <= 'f') {
- visList[x_temp][y_temp][tempNode[2]] = 1;
- arrList[end] = (int *)malloc(sizeof(int) * 4);
- arrList[end][0] = x_temp;
- arrList[end][1] = y_temp;
- int temp = tempNode[2];
- SET_BIT(temp, grid[x_temp][y_temp] - 'a');
- arrList[end][2] = temp;
- arrList[end][3] = tempNode[3] + 1;
- end++;
- }
- // 遇到锁时,如果没钥匙,不做操作,如果有钥匙,就标记并入队
- if (grid[x_temp][y_temp] >= 'A' && grid[x_temp][y_temp] <= 'F') {
- int temp = tempNode[2];
- if (GET_BIT(temp, grid[x_temp][y_temp] - 'A') == 1) {
- visList[x_temp][y_temp][tempNode[2]] = 1;
- arrList[end] = (int *)malloc(sizeof(int) * 4);
- arrList[end][0] = x_temp;
- arrList[end][1] = y_temp;
- arrList[end][2] = tempNode[2];
- arrList[end][3] = tempNode[3] + 1;
- end++;
- }
- }
- }
- // 遍历过的节点出队
- start++;
- }
- // 继续下一层的广度优先遍历
- BFS(grid, arrList, start, end, keyNum, minPath, visList, gridSize, colSize);
- return;
- }
- int shortestPathAllKeys(char ** grid, int gridSize){
- int colSize = strlen(grid[0]);
- int keyNum = 0;
- // visList是标记数组,前两个维度是x,y轴坐标,后一个维度是钥匙状态,注意初始化
- int ***visList = (int ***)malloc(sizeof(int **) * gridSize);
- memset(visList, 0, sizeof(int **) * gridSize);
- for (int i = 0; i < gridSize; i++) {
- visList[i] = (int **)malloc(sizeof(int *) * colSize);
- memset(visList[i], 0, sizeof(int *) * colSize);
- for (int j = 0; j < colSize; j++) {
- visList[i][j] = (int *)malloc(sizeof(int) * 63);
- memset(visList[i][j], 0, sizeof(int) * 63);
- }
- }
- // 充当队列的数组,数组元素包括坐标、当前找到的钥匙状态和走过的路径长度
- int **arrList = (int **)malloc(sizeof(int *) * gridSize * colSize * 63);
- memset(arrList, 0, sizeof(int *) * gridSize * colSize * 63);
- // 获取开始节点的位置等信息
- arrList[0] = getKeyNumAndStart(grid, gridSize, colSize, &keyNum);
- // 初始化返回值
- int minPath = -1;
- // 标记数组中的首节点设为1
- visList[arrList[0][0]][arrList[0][1]][arrList[0][2]] = 1;
- // 开始广度优先遍历
- BFS(grid, arrList, 0, 1, keyNum, &minPath, visList, gridSize, colSize);
-
- return minPath;
- }
-
-
-
- 作者:小迅
- 链接:https://leetcode.cn/problems/shortest-path-to-get-all-keys/solutions/1960590/yan-du-you-xian-sou-suo-zhu-shi-chao-ji-75r4k/
- 来源:力扣(LeetCode)
- 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。