题目链接
题目描述
给定一个 的矩阵迷宫。矩阵中第
行
列的格子的权值为
。
LH 从起点 出发,目标是到达出口
。迷宫配有一个计数器,其初始值为起点
的权值
。
LH 每秒必须从当前格子向上下左右四个相邻方向之一移动一格(不得停留或走出迷宫)。假设 LH 从 移动到
,计数器会累加
。
只有当 LH 身处出口 ,且计数器的总值
恰好满足
时,大门才会打开。
请计算 LH 逃离迷宫所需的最短时间(秒)。如果无论如何都无法逃离,则输出 。
解题思路
本题要求解在满足特定模数条件下,从起点到终点的最短路径。这是一个典型的广度优先搜索 (BFS) 问题,但需要对状态进行扩展。
问题的核心在于,能否到达终点不仅取决于位置,还取决于累积的权值和。由于退出条件只关心总权值对 的余数,我们可以将搜索的状态定义为一个三元组
,其中:
是 LH 当前所在的坐标。
是当前计数器总值对
取模的余数。
由于我们寻求的是最短时间,且每次移动耗时固定为 1 秒,广度优先搜索 (BFS) 是解决此类问题的理想算法。BFS 能够保证我们第一次到达某个状态时,所用的时间(步数)是最短的。
状态转移的关键:权值简化
状态转移时,需要计算新位置的权值并更新余数。权值 的计算看起来非常复杂,但由于我们只关心它对
的模,可以利用模运算的性质进行简化。
根据模运算的基本性质,。
对于任意大于等于 1 的整数
,都有
。
在题目中,权值公式的指数部分为 。由于输入保证
,所以
。因此,指数部分总是大于等于 1。
这意味着
。
于是,每个格子的权值对 取模后可以惊人地简化:
因此,在计算累计余数时,我们只需要考虑 的值,而
和
本身(除了作为模数
外)变得无关紧要。
算法流程
-
初始化:
- 创建一个三维数组
,记录到达状态
的最短时间,所有元素初始化为
。
- 创建一个队列用于 BFS。
- 设
。
- 初始状态:时间
,位置为
(采用 0-based 索引)。计数器值为
,其初始余数
。
- 更新初始状态的距离:
,并将初始状态
入队。
- 创建一个三维数组
-
BFS 搜索:
- 当队列不为空时,出队一个状态
。
- 获取到达该状态的时间
。
- 对于当前位置的四个相邻格子
:
- 计算新的余数:
。
- 如果状态
尚未被访问过(即
):
- 更新该状态的最短时间:
。
- 将新状态
入队。
- 更新该状态的最短时间:
- 计算新的余数:
- 当队列不为空时,出队一个状态
-
获取结果:
- BFS 结束后,我们需要找到达终点
且满足余数条件的最短时间。
- 目标余数是
。
- 因此,最终答案就是
。若该值仍为
,则表示无法逃离。
- BFS 结束后,我们需要找到达终点
代码
#include <iostream>
#include <vector>
#include <queue>
using namespace std;
struct State {
int x, y, rem;
};
int main() {
int n, m, p;
cin >> n >> m >> p;
vector<vector<int>> a(n, vector<int>(m));
for (int i = 0; i < n; ++i) {
for (int j = 0; j < m; ++j) {
cin >> a[i][j];
}
}
// b矩阵在计算余数时是不需要的,但仍需读入以消耗输入
vector<vector<int>> b(n, vector<int>(m));
for (int i = 0; i < n; ++i) {
for (int j = 0; j < m; ++j) {
cin >> b[i][j];
}
}
if (p == 1) { // p-1 = 0, 无法取模
cout << -1 << endl;
return 0;
}
int mod = p - 1;
vector<vector<vector<int>>> dist(n, vector<vector<int>>(m, vector<int>(mod, -1)));
queue<State> q;
int start_rem = a[0][0] % mod;
dist[0][0][start_rem] = 0;
q.push({0, 0, start_rem});
int dx[] = {0, 0, 1, -1};
int dy[] = {1, -1, 0, 0};
while (!q.empty()) {
State curr = q.front();
q.pop();
int t = dist[curr.x][curr.y][curr.rem];
for (int i = 0; i < 4; ++i) {
int nx = curr.x + dx[i];
int ny = curr.y + dy[i];
if (nx >= 0 && nx < n && ny >= 0 && ny < m) {
int next_rem = (curr.rem + a[nx][ny]) % mod;
if (dist[nx][ny][next_rem] == -1) {
dist[nx][ny][next_rem] = t + 1;
q.push({nx, ny, next_rem});
}
}
}
}
cout << dist[n - 1][m - 1][0] << endl;
return 0;
}
import java.util.Scanner;
import java.util.Queue;
import java.util.LinkedList;
import java.util.Arrays;
public class Main {
static class State {
int x, y, rem;
State(int x, int y, int rem) {
this.x = x;
this.y = y;
this.rem = rem;
}
}
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
int m = sc.nextInt();
int p = sc.nextInt();
int[][] a = new int[n][m];
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
a[i][j] = sc.nextInt();
}
}
// b矩阵在计算余数时是不需要的,但仍需读入以消耗输入
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
sc.nextInt();
}
}
if (p == 1) { // p-1 = 0, 无法取模
System.out.println(-1);
return;
}
int mod = p - 1;
int[][][] dist = new int[n][m][mod];
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
Arrays.fill(dist[i][j], -1);
}
}
Queue<State> q = new LinkedList<>();
int startRem = a[0][0] % mod;
dist[0][0][startRem] = 0;
q.offer(new State(0, 0, startRem));
int[] dx = {0, 0, 1, -1};
int[] dy = {1, -1, 0, 0};
while (!q.isEmpty()) {
State curr = q.poll();
int t = dist[curr.x][curr.y][curr.rem];
for (int i = 0; i < 4; i++) {
int nx = curr.x + dx[i];
int ny = curr.y + dy[i];
if (nx >= 0 && nx < n && ny >= 0 && ny < m) {
int nextRem = (curr.rem + a[nx][ny]) % mod;
if (dist[nx][ny][nextRem] == -1) {
dist[nx][ny][nextRem] = t + 1;
q.offer(new State(nx, ny, nextRem));
}
}
}
}
System.out.println(dist[n - 1][m - 1][0]);
}
}
import sys
from collections import deque
def solve():
try:
n, m, p = map(int, input().split())
a = [list(map(int, input().split())) for _ in range(n)]
# b矩阵在计算余数时是不需要的,但仍需读入以消耗输入
for _ in range(n):
list(map(int, input().split()))
if p == 1: # p-1 = 0, 无法取模
print(-1)
return
mod = p - 1
dist = [[[-1] * mod for _ in range(m)] for _ in range(n)]
q = deque()
start_rem = a[0][0] % mod
dist[0][0][start_rem] = 0
q.append((0, 0, start_rem))
dx = [0, 0, 1, -1]
dy = [1, -1, 0, 0]
while q:
x, y, rem = q.popleft()
t = dist[x][y][rem]
for i in range(4):
nx, ny = x + dx[i], y + dy[i]
if 0 <= nx < n and 0 <= ny < m:
next_rem = (rem + a[nx][ny]) % mod
if dist[nx][ny][next_rem] == -1:
dist[nx][ny][next_rem] = t + 1
q.append((nx, ny, next_rem))
print(dist[n - 1][m - 1][0])
except (IOError, ValueError):
# 处理可能的输入结束或格式错误
pass
solve()
算法及复杂度
-
算法:广度优先搜索 (BFS)
-
时间复杂度:
。状态的总数为
。在 BFS 中,每个状态最多被访问一次,每次访问会尝试向四个方向扩展,因此总时间复杂度与状态总数成正比。
-
空间复杂度:
。空间开销主要来自于存储所有状态最短距离的
数组,以及 BFS 队列。
-
空间复杂度:
。空间开销主要来自于存储所有状态最短距离的
数组,以及 BFS 队列。