📜  门| GATE-CS-2016(Set 1)|问题22(1)

📅  最后修改于: 2023-12-03 15:12:43.036000             🧑  作者: Mango

门 | GATE-CS-2016(Set 1)|问题22
问题描述

已知两个nxn矩阵A和B,这些矩阵中的每个元素都是0或1。从$A_{i,1}$开始,沿着若干步可以到达$A_{i,n}$。我们定义一个门是一个包含两个0(从上门和下门)和两个1的元素的1*2矩阵。要想让一个人通过一个门,必须在相应的元素上停留一步。该人只能从$A_{i,j}$经过一个门到$A_{k+1,j}$,如果$B_{i,j} = B_{k+1,j} = 1$。这里, 0 ≤ i < k < n, 1 ≤ j < n。

请提供从一个n x n矩阵A到达另一个nxn矩阵B所需的最短步数的C程序。

函数原型如下:

int minSteps(int A[][n], int B[][n], int n);
解法思路

这是计算图中一道最短路径问题的变种。我们可以将矩阵$A$看做一个源点,将矩阵$B$看做是一个终点。图中点的集合可以表示为$V = R \times C$,其中$R={0,1,...,n-1},C={1,2,...,n}$。根据题干中门的限制,边的集合可以表示为$E$,其中$(i,j) → (k+1, j)$可以被加入到$E$中仅当$A_{i,j} = B_{k+1,j} = 1$,而且两个点必须关联着一个门。

一旦我们有了这样的图,我们可以使用广度优先搜索(BFS)来计算从$A$到$B$的最短路径。即从$A$出发,广度优先搜索图,直到到达$B$为止。我们可以加入一些优化,使得搜索过程中的过多的状态被移除。一个开销较高的优化方法:对于已经访问过的节点,我们从队列中剔除并且不予处理。

代码实现
#include <stdio.h>
#define MAX_QUEUE_SIZE 1000000

int directions[][2] = {{-1, 0}, {0, -1}, {0, 1}, {1, 0}};

int hasDoor(int A[][n], int B[][n], int i, int k, int j) {
  if (A[i][j] == 1 && A[k+1][j] == 1 && B[i][j] == 1 && B[k+1][j] == 1) {
    return 1;
  }
  return 0;
}

void push(int** queue, int* size, int i, int j, int steps) {
  queue[*size][0] = i;
  queue[*size][1] = j;
  queue[*size++][2] = steps;
}

int* head(int** queue) {
  return queue[0];
}

void pop(int** queue, int* size) {
  int i, j, steps;
  for (int k = 0; k < *size-1; k++) {
    queue[k] = queue[k+1];
  }
  (*size)--;
}

int empty(int* size) {
  if (*size <= 0) {
    return 1;
  } else {
    return 0;
  }
}

int minSteps(int A[][n], int B[][n], int n) {
  int** queue;
  *queue = (int**)malloc(sizeof(int)*n*n*3);
  int size = 0;
  int steps = 0;
  int visited[n][n] = {{0}};
  int popSize = 0;
  int popNeeded = 1;
  push(queue, &size, 0, 0, 0);
  visited[0][0] = 1;
  while (empty(&size) == 0) {
    if (popSize == popNeeded) {
      pop(queue, &popSize);
      popNeeded--;
    }
    int* current = head(queue);
    if (current[0] == n-1 && current[1] == n-1) {
      return current[2];
    }
    for (int d = 0; d < 4; d++) {
      int nextI = current[0] + directions[d][0];
      int nextJ = current[1] + directions[d][1];
      if (nextI >= 0 && nextI < n && nextJ >= 0 && nextJ < n && visited[nextI][nextJ] == 0 && hasDoor(A, B, current[0], nextI, current[1])) {
        visited[nextI][nextJ] = 1;
        push(queue, &size, nextI, nextJ, current[2] + 1);
        popNeeded++;
      }
    }
    popSize++;
  }
  return -1;
}