1 条题解
-
0
思路
把网格看成一张图:
- 每个非障碍格子是一个点;
- 上下左右相邻的两个非障碍格子之间连一条边权为 的边;
- 同一字母的两个传送门格子之间连一条边权为 的边——站到任意一个传送门上,都可以不花步数走到配对的传送门。传送门格本身和普通格一样可以通行,也可以从传送门格继续上下左右移动。
于是问题变成从 到 的最短路。图中边权只有 和 ,所以用 0-1 BFS:
- 用双端队列维护待扩展的点,起点入队,距离为 ;
- 每次取出队首 ,若已经处理过就跳过,否则标记为已处理。每个点第一次被取出时,它的距离就是最短路;
- 权边(传送):若当前格是传送门,把配对格松弛为 ,松弛成功就压入队首;
- 权边(上下左右):松弛成功就压入队尾。
因为 权边压队首、 权边压队尾,队列中的距离始终最多相差 ,出队顺序按距离非降,所以第一次取出某个点时它的距离已经最优。到达终点时直接输出 ;队列空了还没到达则输出 。
注意不能写成普通 FIFO 队列: 权边会让先入队的点带着更大的距离,出队顺序不再按距离单调,此时"一弹出终点就返回"可能得到偏大的答案。
实现上先扫描一遍地图,把每种字母的两个格子记下来建立双向的传送映射;
vis保证每个点只按最终距离扩展一次,避免重复入队带来的额外开销。时间复杂度 ,空间 。
代码
#include<bits/stdc++.h> #define int long long #define pll pair<int,int> using namespace std; const int N = 1010, inf = 1e18; int dx[4] = {1,-1,0,0}, dy[4] = {0,0,1,-1}; // 上下左右四个方向 char a[N][N]; // 地图,'#' 表示障碍 int dp[N][N]; // 到达每个格子的最少步数 bool vis[N][N]; // 该格子是否已按最终距离扩展过 int n,m; void go(){ // 初始化:地图外一律视为障碍,距离设为无穷大 for(int i=0;i<N;i++){ for(int j=0;j<N;j++){ a[i][j] = '#'; dp[i][j] = inf; vis[i][j] = false; } } vector<vector<pll>>coor(100); // coor[c] 存放字母 c 对应的传送门坐标 cin>>n>>m; for(int i=1;i<=n;i++){ for(int j=1;j<=m;j++){ cin>>a[i][j]; // 小写字母是传送门,按字母归类 if('a' <= a[i][j] and a[i][j] <= 'z'){ coor[a[i][j]-'a'].push_back({i, j}); } } } // 建立传送门映射:同字母的两个格子互相传送 map<pll, pll>door; for(vector<pll> v:coor){ if(v.size()==2){ door[v[0]] = v[1]; door[v[1]] = v[0]; } } struct node { int x; int y; }; // 0-1 BFS:0 权边压入队首,1 权边压入队尾 deque<node>q; q.push_back({1, 1}); dp[1][1] = 0; // 起点步数为 0 while (q.size()) { auto [x, y] = q.front(); q.pop_front(); // 每个点只按最终的最短距离扩展一次 if (vis[x][y]) continue; vis[x][y] = true; // 第一次弹出终点就是最短路 if (x == n && y == m) { cout << dp[x][y] << endl; return; } // 逻辑 1:处理传送(0 权边) if ('a' <= a[x][y] && a[x][y] <= 'z') { auto [mx, my] = door[{x, y}]; // 传送不耗时,所以步数和当前一样 if (dp[mx][my] > dp[x][y]) { dp[mx][my] = dp[x][y]; q.push_front({mx, my}); // 0 权边放队首 } } // 逻辑 2:处理常规上下左右移动(1 权边) for (int i = 0; i < 4; i++) { int nx = x + dx[i], ny = y + dy[i]; // 越界或是障碍就不能走 if (nx < 1 || nx > n || ny < 1 || ny > m || a[nx][ny] == '#') continue; if (dp[nx][ny] > dp[x][y] + 1) { dp[nx][ny] = dp[x][y] + 1; q.push_back({nx, ny}); // 1 权边放队尾 } } } // 队列为空仍未到达终点,说明无路可走 cout<<-1<<endl; } signed main() { ios::sync_with_stdio(false); cin.tie(nullptr);cout.tie(nullptr); int _=1; // 本题只有一组数据,不需要读入测试组数 while(_--){ go(); } return 0; }
- 1
信息
- ID
- 15
- 时间
- 1000ms
- 内存
- 256MiB
- 难度
- 4
- 标签
- 递交数
- 0
- 已通过
- 0
- 上传者