[洛谷] P2802 回家

本文深入探讨了深度优先搜索(DFS)算法及其在解决迷宫路径寻找问题中的应用,通过回溯技巧优化搜索效率,详细解析了代码实现细节。

 

dfs 标记 + 回溯

//#pragma GCC optimize(2)
#include <cstdio>
#include <iostream>
#include <cstdlib>
#include <cmath>
#include <cctype>
#include <string>
#include <cstring>
#include <algorithm>
#include <stack>
#include <queue>
#include <set>
#include <map>
#include <ctime>
#include <vector>
#include <fstream>
#include <list>
#include <iomanip>
#include <numeric>
using namespace std;
typedef long long ll;

const int MAXN = 1e2 + 10;

int n, m, bx, by;

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

int arr[MAXN][MAXN], ans = 0x3f3f3f3f;

int used[MAXN][MAXN];

void dfs(int x, int y, int hp, int step)
{
    if(hp == 0 || arr[x][y] == 0 || used[x][y] == 1)
        return ;

    if(!(x >= 0 && x <= n && y >= 0 && y <= m))
        return ;

    if(arr[x][y] == 3)
    {
        ans = min(ans, step);
        return ;
    }

    if(arr[x][y] == 4)
        hp = 6;
    
    used[x][y] = 1;
    for(int i = 0; i < 4; i++)
    {

        dfs(x + movel[i][0], y + movel[i][1], hp - 1, step + 1);
    }
    used[x][y] = 0;
}

int main()
{
    //ios::sync_with_stdio(false);

    //cin.tie(0);     cout.tie(0);

    cin>>n>>m;

    for(int i = 0; i < n; i++)
        for(int j = 0; j < m; j++)
        {
            cin>>arr[i][j];
            if(arr[i][j] == 2)
                bx = i, by = j;
        }
    
    dfs(bx, by, 6, 0);

    ans >= 0x3f3f3f3f ? cout<<"-1"<<endl : cout<<ans<<endl;

    return 0;
}

 

### BFS 算法在洛谷 P2802 中的应用解析 洛谷 P2802 是一个典型的迷宫问题,其中小 H 需要在 n×m 的网格中找到一条从起点到家(目标点)的最短路径,同时考虑血量限制和拾取补给的规则。广度优先搜索(BFS)是一种适用于此类问题的算法,尤其适合寻找最短路径[^2]。 #### 1. **BFS 的基本思想** BFS 通过逐层扩展节点来遍历图结构,直到找到目标点为止。每一层代表移动一步后可以到达的所有位置。这种方法能够保证第一次到达目标点时所使用的步数是最少的,因此适用于需要找最短路径的问题[^2]。 #### 2. **状态表示与队列设计** 在常规的 BFS 中,只需要记录坐标即可。但在本题中,由于存在血量、是否拾取鼠标等额外条件,因此每个状态不仅需要包含坐标信息,还需要包括当前血量以及是否已经拾取过鼠标。 - 每个状态可以用 `struct` 或 `tuple` 表示为:`(x, y, hp, has_mouse)`。 - 使用 `queue` 存储当前待处理的状态。 - 使用 `visited[x][y][hp]` 来标记是否已经访问过某个状态,避免重复搜索。 #### 3. **剪枝策略** 为了提高效率,需要进行剪枝: - 如果当前血量为 0,直接跳过该状态。 - 如果当前位置是家(目标点),则返回当前步数作为答案。 - 如果当前位置有鼠标,则更新状态为满血,并标记该格子已被拾取。 - 如果当前位置已经被访问过且以相同的血量进入,则不再处理该状态。 #### 4. **实现代码示例** ```cpp #include <iostream> #include <queue> #include <vector> #include <cstring> using namespace std; int n, m; char grid[10][10]; bool visited[10][10][7]; // x, y, hp int dx[] = {-1, 0, 1, 0}; int dy[] = {0, 1, 0, -1}; struct State { int x, y, hp, steps; }; bool inBounds(int x, int y) { return x >= 0 && x < n && y >= 0 && y < m; } int bfs() { queue<State> q; q.push({0, 0, 6, 0}); visited[0][0][6] = true; while (!q.empty()) { State curr = q.front(); q.pop(); if (curr.hp == 0) continue; if (grid[curr.x][curr.y] == '3') { return curr.steps; // 到达终点 } for (int i = 0; i < 4; ++i) { int nx = curr.x + dx[i]; int ny = curr.y + dy[i]; if (inBounds(nx, ny)) { int new_hp = curr.hp - 1; if (new_hp < 0) continue; if (grid[nx][ny] == '1') new_hp = 6; // 拾取鼠标 if (!visited[nx][ny][new_hp]) { visited[nx][ny][new_hp] = true; q.push({nx, ny, new_hp, curr.steps + 1}); } } } } return -1; // 无法回家 } int main() { cin >> n >> m; for (int i = 0; i < n; ++i) for (int j = 0; j < m; ++j) cin >> grid[i][j]; memset(visited, false, sizeof(visited)); cout << bfs() << endl; return 0; } ``` #### 5. **时间与空间复杂度分析** - 时间复杂度:O(n × m × HP),其中 HP 最大为 6。 - 空间复杂度:同样为 O(n × m × HP),用于存储访问状态。 #### 6. **注意事项** - 起点默认为 `(0, 0)`,家的位置为 `'3'`。 - 移动前必须判断是否还有足够的血量(至少 1 点)。 - 每次经过鼠标格子都可以恢复血量,因此不能标记鼠标格子为已使用,而是每次进入都可能更新状态。 - 在家门口死亡的情况不能算作成功回家
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值