BFS-剪枝走迷宫问题【迷宫与宝藏-OJ1646】

参考博客

https://blog.csdn.net/qq_34542903/article/details/51100015

 

 

题目描述

机器人要在一个矩形迷宫里行动(不能原地停留,只能走向上/下/左/右),每移动一格花费1个单位时间。

迷宫有以下几种元素: 【*】 机器人的起点 【#】 墙。机器人不能走过这些格子 【.】 平地。

机器人可以在上面自由行走 【0-9】 宝藏。当机器人走到此处会立刻获得该数字相应的宝藏,宝藏不会消失,可以反复获取(但不能停留) 若机器人要恰好获得总和为x的宝藏,它最少需要多少时间?

 

输入要求

 

第一行输入任务数量T, 接下来有T个任务 每块第一行有两个整数, n( < 100), m( < 100), 表示迷宫有n+1行和m+1列,输出迷宫地图。 最后一行输入你要收集的宝藏的总价值x(x ≤ 100)

 

输出要求

对于每个任务,输出最少花费的时间,如果完成不了该任务则输出-1

 

输入样例

3

2 3

1.#2

#..#

*.#.

3

2 3

2.#2

#..#

*.#.

5

2 3

2.#2

#.3#

*.#.

5

 

输出样例

8

-1

6

 

思路分析

类似走迷宫的搜索问题,对于数据较大的一般用BFS解决

难点是需要在收集相应数目的金币的同时要求所走步数最小

可以这样理解,在地图上每走一步都有相应的值对应,可走的道路可以看作值为0的金币堆

通过深度搜索的形式不断向外可走的地方探寻搜索,直到达到目标金币数

因此需要一个记录金币数的数组,即可通过一个三维数组实现记录当前位置的同时记录当前金币数

需要注意的有两点

1.在搜索往下进行时,如果此时拥有的金币数大于目标金币数,应该结束继续往下的搜索,减少不必要的时间消耗(剪枝)。

2.同走迷宫不同,对于会重复经过同一位置的情况,需要多加一组判断,如果达到下一步的消耗的步数大于此时位置的步数+1,则走这一步,并将之推入队列。即通过当前位置走到下一步的位置是步数最短的情况。

 

AC代码

#include<stdio.h>
#include<string.h>
#include<queue>
#define Inf 99999
#define MAX 110
using namespace std;
int dir[4][2] = {0,-1,0,1,-1,0,1,0};//方向变换
char Map[MAX][MAX];//记录地图
int Step[MAX][MAX][MAX];//记录相应位置收集到不同金币数所花费的步数,除起始点外,其他位置初始为无穷大步
int column,row;//行、列
int target;//目标钱数
typedef struct
{
    int px;
    int py;
    int money;
}Pt;//位置坐标

queue<Pt>que;//位置队列
int BFS()
{
    int i;
    Pt Bg;
    Pt Next;
    while(!que.empty())
    {
        Pt Bg = que.front();//读取队列最上方元素,即当前所处位置
        que.pop();//将其移出队列
        for(i = 0; i < 4; i++)
        {
           Next.px = Bg.px + dir[i][0];
           Next.py = Bg.py + dir[i][1];//转向后位置坐标
            if(Next.px >= 0&&Next.px <= row && Next.py >= 0&& Next.py <= column&&Map[Next.px][Next.py] != '#')
            {
                Next.money = Bg.money + Map[Next.px][Next.py] - '0';//转向后钱数为:之前所拥有钱数+当前位置钱数
                if(Next.money == target)//如果走一步后达到目标钱数
                    return Step[Bg.px][Bg.py][Bg.money] + 1;//返回当前步数+1
                if(Next.money > target)//如果下一步得到的钱数过多,跳过向这边搜索(剪枝),继续循环
                    continue;
                if(Step[Next.px][Next.py][Next.money] > Step[Bg.px][Bg.py][Bg.money] + 1)//如果原来的步数+1小于下一步所花的步数
                {
                    Step[Next.px][Next.py][Next.money] = Step[Bg.px][Bg.py][Bg.money] + 1;//将原步数+1赋值给下一步中
                    que.push(Next);//将下一步推入队列
                }
            }
        }
    }
    return -1;//未找到方法得到所求钱数
}
int main()
{

    int T,i,j,k;
    Pt start;
    scanf("%d",&T);
    while(T--)
    {
        while(!que.empty())//将队列中现存的元素全部推出
        {
            que.pop();
        }
        scanf("%d %d",&row,&column);
        memset(Map,0,sizeof(Map));
        for(i = 0; i <= row; i++)
        scanf("%s",Map[i]);
        scanf("%d",&target);
        
        for(i = 0; i <= row; i++)
            for(j = 0; j <= column; j++)
                for(k = 0; k <= target; k++ )
                Step[i][j][k] = Inf;//给步数赋初值为无穷大
                
        for(i = 0; i <= row; i++)//重新初始化地图,记录起始点,对于每一位置赋值钱数
            for(j = 0; j <= column; j++)
        {
            if(Map[i][j] == '*')//起始点
            {
                Map[i][j] = '0';//起始点可通过,获得钱数为0
                start.px = i;
                start.py = j;//记录起始位置
                start.money = 0;//记录起始钱数
                Step[i][j][0] = 0;//当前步数为0
                que.push(start);//将起始点推入队列
            }
            if(Map[i][j] == '.')
            {
                Map[i][j] = '0';//'.'为可通过,经过此位置得到钱数为0
            }
        }
        if(target == 0)//特例判断,当target = 0时会返回-1,在这里特判一下
            printf("0\n");
        else
        printf("%d\n",BFS());
    }
    return 0;
}

 

 

总结和分析

有时候思路不需要太过复杂,多看多学,考虑以空间换时间。

第一次思考的时候是这样想的

1.用BFS求出起点到各宝藏点的距离
2.宝藏点到其他宝藏点的距离
3.从起点开始DP

结果事实证明错的离谱,需要注意和学习的是要有数据化题目的能力。

主要就是一点没看透:"."所代表的通路其实就是金币数为0的位置。

 

 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值