From c0d8b485d0ca33c5e862deb03c5c3023d87ea48e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E6=B5=B7?= <10402852@qq.com> Date: Tue, 2 Jan 2024 08:31:14 +0800 Subject: [PATCH] 'commit' --- .../AcWing_TiGao/T3/MiniPathExtend/1131.cpp | 46 ++++++++-------- .../AcWing_TiGao/T3/MiniPathExtend/1131.md | 53 ++++++++++--------- 2 files changed, 50 insertions(+), 49 deletions(-) diff --git a/TangDou/AcWing_TiGao/T3/MiniPathExtend/1131.cpp b/TangDou/AcWing_TiGao/T3/MiniPathExtend/1131.cpp index a0551f5..8c5ef11 100644 --- a/TangDou/AcWing_TiGao/T3/MiniPathExtend/1131.cpp +++ b/TangDou/AcWing_TiGao/T3/MiniPathExtend/1131.cpp @@ -6,7 +6,7 @@ typedef pair PII; int g[N][N]; // 两个位置之间的间隔是什么,可能是某种门,或者是墙 int key[N]; // 某个坐标位置上有哪些钥匙,这是用数位压缩记录的,方便位运算 -int dist[N][1 << M]; // 哪个位置,在携带不同的钥匙情况下的状态 +int dis[N][1 << M]; // 哪个位置,在携带不同的钥匙情况下的状态 int n, m; // n行m列 int k; // 迷宫中门和墙的总数 int p; // p类钥匙 @@ -19,22 +19,23 @@ int get(int x, int y) { } int bfs() { - memset(dist, 0x3f, sizeof dist); // 初始化距离 - queue q; // bfs用的队列 + memset(dis, 0x3f, sizeof dis); // 初始化距离 + queue q; // bfs用的队列 - int t = get(1, 1); // 从编号1出发 - q.push({t, key[t]}); // 位置+携带钥匙的压缩状态 = 现在的真正状态 - dist[t][key[t]] = 0; // 初始状态的距离为0 + int S = get(1, 1); // 从编号1出发 + q.push({S, key[S]}); // 位置+携带钥匙的压缩状态 = 现在的真正状态 + dis[S][key[S]] = 0; // 初始状态的需要走的步数为0 while (q.size()) { PII x = q.front(); q.pop(); int u = x.first; // 出发点编号 - int st = x.second; // 钥匙状态 + int st = x.second; // 钥匙状态,为状态压缩的数字 - // 找到大兵瑞恩就结束了 - if (u == n * m) return dist[u][st]; + // dis[u][st]:到达了n*m,并且,当前状态是st: 找到大兵瑞恩就结束了,不用管最终的钥匙状态是什么 + // 是什么都是符合拯救大兵的目标的 + if (u == n * m) return dis[u][st]; // 四个方向 for (int i = 0; i < 4; i++) { @@ -42,27 +43,26 @@ int bfs() { int tx = (u - 1) / m + 1 + dx[i]; // 下一个位置 int ty = (u - 1) % m + 1 + dy[i]; - int tz = get(tx, ty); // 要去的坐标位置tz - int ts = st; // 复制出z结点携带过来的钥匙状态 + int T = get(tx, ty); // 要去的坐标位置T /* - g[z][tz] == 0 有墙,不能走 - g[z][tz] > 0 有门,有钥匙能走,无钥匙不能走 - g[z][tz] == -1 随便走 + g[z][T] == 0 有墙,不能走 + g[z][T] > 0 有门,有钥匙能走,无钥匙不能走 + g[z][T] == -1 随便走 */ - // 出界或有墙 - if (tx == 0 || ty == 0 || tx > n || ty > m || g[u][tz] == 0) continue; + // 出界或有墙,没有办法转移 + if (tx == 0 || ty == 0 || tx > n || ty > m || g[u][T] == 0) continue; - // 有门,并且, v这个状态中没有当前类型的钥匙 - if (g[u][tz] > 0 && !(st >> g[u][tz] & 1)) continue; + // 有门,并且, st这个状态中没有带过来当前类型的钥匙 + if (g[u][T] > 0 && !(st >> g[u][T] & 1)) continue; - // 捡起钥匙 - ts |= key[tz]; + // 捡起钥匙不会增加成本,所以,无条件捡起来钥匙 + int ST = st | key[T]; // 如果这个状态没有走过 - if (dist[tz][ts] == INF) { - q.push({tz, ts}); // 入队列 - dist[tz][ts] = dist[u][st] + 1; // 步数加1 + if (dis[T][ST] == INF) { + q.push({T, ST}); // 入队列 + dis[T][ST] = dis[u][st] + 1; // 步数加1 } } } diff --git a/TangDou/AcWing_TiGao/T3/MiniPathExtend/1131.md b/TangDou/AcWing_TiGao/T3/MiniPathExtend/1131.md index 7e30689..8654c54 100644 --- a/TangDou/AcWing_TiGao/T3/MiniPathExtend/1131.md +++ b/TangDou/AcWing_TiGao/T3/MiniPathExtend/1131.md @@ -77,12 +77,12 @@ $1≤k≤150$ ### 二、解题思路 -试想下如果本题 **没有钥匙和门** 的条件,只要求从 **左上角** 走到 **右下角** 的最小步数,就是简单的迷宫问题了,可以使用$BFS$解决。 +试想下如果本题 **没有钥匙和门** 的条件,只要求从 **左上角** 走到 **右下角** 的最小步数,就是简单的迷宫问题了,可以使用$bfs$解决。 #### 状态表示 -加上钥匙和门的的条件,便是**类似于八数码问题**了。实际上$BFS$解决的最短路问题都可以看作**求从初始状态到结束状态需要的最小转移次数**: +加上钥匙和门的的条件,便是**类似于八数码问题**了。实际上$bfs$解决的最短路问题都可以看作 **求从初始状态到结束状态需要的最小转移次数**: -普通迷宫问题的 **状态** 就是 **当前所在的坐标**,八数码问题的 **状态** 就是**当前棋盘的局面**。 +普通迷宫问题的 **状态** 就是 **当前所在的坐标**,八数码问题的 **状态** 就是 **当前棋盘的局面**。 本题在迷宫问题上加上了 **钥匙和门** 的条件,显然,处在同一个坐标下,**持有钥匙和不持有钥匙就不是同一个状态了**,为了能够清楚的表示每个状态,除了当前坐标外还需要加上当前获得的钥匙信息,即$f[x][y][st]$表示当前处在$(x,y)$位置下持有钥匙状态为$st$,将二维坐标压缩成一维就得到$f[z][st]$这样的状态表示了,或者说,$z$是格子的编号,从上到下,从左而右的编号依次为$1$到$n*m$,$st$为$0110$时,表示持有第$1,2$类钥匙,这里注意我在 表示状态时抛弃了最右边的一位,因为钥匙编号从$1$开始,我想确定是否持有第$i$类钥匙时,只需要判断`st >> i & 1`是不是等于$1$即可。 @@ -108,7 +108,7 @@ typedef pair PII; int g[N][N]; // 两个位置之间的间隔是什么,可能是某种门,或者是墙 int key[N]; // 某个坐标位置上有哪些钥匙,这是用数位压缩记录的,方便位运算 -int dist[N][1 << M]; // 哪个位置,在携带不同的钥匙情况下的状态 +int dis[N][1 << M]; // 哪个位置,在携带不同的钥匙情况下的状态 int n, m; // n行m列 int k; // 迷宫中门和墙的总数 int p; // p类钥匙 @@ -121,22 +121,23 @@ int get(int x, int y) { } int bfs() { - memset(dist, 0x3f, sizeof dist); // 初始化距离 - queue q; // bfs用的队列 + memset(dis, 0x3f, sizeof dis); // 初始化距离 + queue q; // bfs用的队列 - int t = get(1, 1); // 从编号1出发 - q.push({t, key[t]}); // 位置+携带钥匙的压缩状态 = 现在的真正状态 - dist[t][key[t]] = 0; // 初始状态的距离为0 + int S = get(1, 1); // 从编号1出发 + q.push({S, key[S]}); // 位置+携带钥匙的压缩状态 = 现在的真正状态 + dis[S][key[S]] = 0; // 初始状态的需要走的步数为0 while (q.size()) { PII x = q.front(); q.pop(); int u = x.first; // 出发点编号 - int st = x.second; // 钥匙状态 + int st = x.second; // 钥匙状态,为状态压缩的数字 - // 找到大兵瑞恩就结束了 - if (u == n * m) return dist[u][st]; + // dis[u][st]:到达了n*m,并且,当前状态是st: 找到大兵瑞恩就结束了,不用管最终的钥匙状态是什么 + // 是什么都是符合拯救大兵的目标的 + if (u == n * m) return dis[u][st]; // 四个方向 for (int i = 0; i < 4; i++) { @@ -144,27 +145,26 @@ int bfs() { int tx = (u - 1) / m + 1 + dx[i]; // 下一个位置 int ty = (u - 1) % m + 1 + dy[i]; - int tz = get(tx, ty); // 要去的坐标位置tz - int ts = st; // 复制出z结点携带过来的钥匙状态 + int T = get(tx, ty); // 要去的坐标位置T /* - g[z][tz] == 0 有墙,不能走 - g[z][tz] > 0 有门,有钥匙能走,无钥匙不能走 - g[z][tz] == -1 随便走 + g[z][T] == 0 有墙,不能走 + g[z][T] > 0 有门,有钥匙能走,无钥匙不能走 + g[z][T] == -1 随便走 */ - // 出界或有墙 - if (tx == 0 || ty == 0 || tx > n || ty > m || g[u][tz] == 0) continue; + // 出界或有墙,没有办法转移 + if (tx == 0 || ty == 0 || tx > n || ty > m || g[u][T] == 0) continue; - // 有门,并且, v这个状态中没有当前类型的钥匙 - if (g[u][tz] > 0 && !(st >> g[u][tz] & 1)) continue; + // 有门,并且, st这个状态中没有带过来当前类型的钥匙 + if (g[u][T] > 0 && !(st >> g[u][T] & 1)) continue; - // 捡起钥匙 - ts |= key[tz]; + // 捡起钥匙不会增加成本,所以,无条件捡起来钥匙 + int ST = st | key[T]; // 如果这个状态没有走过 - if (dist[tz][ts] == INF) { - q.push({tz, ts}); // 入队列 - dist[tz][ts] = dist[u][st] + 1; // 步数加1 + if (dis[T][ST] == INF) { + q.push({T, ST}); // 入队列 + dis[T][ST] = dis[u][st] + 1; // 步数加1 } } } @@ -204,4 +204,5 @@ int main() { printf("%d\n", bfs()); return 0; } + ```