洛谷P1301 魔鬼之城(bfs基础)

该博客探讨了如何使用广度优先搜索(BFS)解决洛谷上的P1301挑战,即魔鬼之城问题。探险者从(1, 1)出发,目标是到达(N, M),每间房的墙上标有1到13的魔法数字,决定了跳跃方向。禁止连续两次同向跳跃。博客提供了问题的详细解析和解决方案,旨在帮助读者理解如何运用BFS策略来解决此类路径寻找问题。" 125622280,7475197,Vue中正确实现锚点链接的技巧,"['vue.js', 'javascript', '前端开发']

题目链接
在一个被分割为N*M个正方形房间的矩形魔鬼之城中,一个探险者必须遵循下列规则才能跳跃行动。他必须从(1, 1)进入,从(N, M)走出;在每一房间的墙壁上都写了一个魔法数字,是1~13之内的自然数;探险者可以想像出8个方向中的任何一个(水平或垂直或对角线方向),随后他就可以作一次空间跳跃穿过这一方向上的连续的X个房间,其中X是他原来所在房间的魔法数字。但如果在这一方向上的房间数小于X,则他不作任何跳跃,而必须想像另一个方向。同时,探险者不能作连续两次相同方向的跳跃。

在这里插入图片描述

例如在上图的5*4的魔鬼之城中,如果探险者现在所在的位置是(3, 3),那么通过依次空间跳跃他可以到达下列房间中的一个:(1, 1),(3, 1),(1, 3),(5, 1),或(5, 3)。另外,如果他要用两次跳跃从(5, 4)到达(3, 2),则他不能首先跳到(4, 3)(因为这样他第二次跳跃的方向将和第一次相同,而这是不允许的)。所以他必须先跳跃到(2, 1)。

请你写一个程序,对给定的地图,算出探险者至少需要跳跃多少步才能离开魔鬼之城。

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
using namespace std;
int n,m,sq[105][105];
int dx[8]={-1,-1,0,1,1,1,0,-1};
int dy[8]={0,1,1,1,0,-1,-1,-1};
bool vis[105][105][10];
int ans[105][105];
struct node
{
    int x,y,dir,sum;
}q[100005];//接下来拓展的点的横坐标、纵坐标、方向、步数 
void bfs()
{
    int head=1,tail=1;
    q[tail].x=1;
    q[tail].y=1;
    q[tail].dir=0;
    q[tail].sum=0;
    tail++;
    while(head<tail)
    {
        for(int k=0;k<8;k++)
        {
            if(k!=q[head].dir)
            {
                int nowx=q[head].x+dx[k]*sq[q[head].x][q[head].y];
                int nowy=q[head].y+dy[k]*sq[q[head].x][q[head].y];
                if(nowx<1 || nowx>n || nowy<1 || nowy>m) continue;
                if(!vis[nowx][nowy][k])
                {
                    vis[nowx][nowy][k]=1;
                    ans[nowx][nowy]=q[head].sum+1;
                    q[tail].x=nowx;
                    q[tail].y=nowy;
                    q[tail].dir=k;
                    q[tail].sum=ans[nowx][nowy];
                    tail++;
                }
                if(ans[n][m]>0) return;
            }
        }
        head++;
    }
}
int main()
{
    cin>>n>>m;//列、行
    swap(n,m);//行、列
    for(int i=1;i<=n;i++)
    for(int j=1;j<=m;j++)
    {
        cin>>sq[i][j];
    }
    bfs();
    if(ans[n][m]>0) cout<<ans[n][m]<<endl;
    else cout<<"NEVER"<<endl;
    return 0;
}
/*
int dfs(int x,int y,int sum)
{
	if(sum>ans) return;
	if(x==n&&y==m)
	{
		ans=min(ans,sum);
		return;
	}
	for(int i=1;i<=n;i++)
	for(int j=1;j<=m;j++)
	for(int k=0;k<8;k++)
	{
		int nowx=x+dx[k]*sq[i][j];
		int nowy=y+dy[k]*sq[i][j];
		if(nowx<1 || nowx>n || nowy<1 || nowy>m) continue;
		if(!vis[nowx][nowy] && !flag[k])
		{
			vis[nowx][nowy]=1; flag[k]=1;
			dfs(nowx,nowy,step+1);
			vis[nowx]][nowy]=1; flag[k]=0;
		}
	}
	flag[k]=0;
	return;
}*/
### 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、付费专栏及课程。

余额充值