一日一题:第三题--方格迷宫(DFS小难,不过很详细!)

题目描述

给定一个 n 行 m 列的方格矩阵。

行从上到下依次编号为 1∼n,列从左到右依次编号为 1∼m。

第 i 行第 j 列的方格表示为 (i,j)。

矩阵中的方格要么是空地(用 . 表示),要么是陷阱(用 # 表示)。

初始时,你位于方格 (x1,y1),你需要前往方格 (x2,y2)。

每次移动,你可以任选上、下、左、右四个方向之一,并沿该方向移动 1∼k 步。

从一个方格移动至相邻方格视为一步。

但是,你要保证在你的移动过程中不能走出矩阵,也不能进入陷阱方格。

请你计算从方格 (x1,y1)移动至方格 (x2,y2),所需要的最少移动次数

保证方格 (x1,y1) 和方格 (x2,y2)都是空地。

方格 (x1,y1) 和方格 (x2,y2)可能是同一个方格。

注意:注意区分本题中移动次数与移动步数的区别。

输入格式

第一行包含三个整数 n,m,k。

接下来 nn 行,每行包含 m 个字符,其中第 i 行第 j 个字符,要么是 .,表示方格 (i,j) 是空地;要么是 #,表示方格 (i,j)是陷阱。

最后一行包含四个整数 x1,y1,x2,y2。

输出格式

一个整数,表示所需要的最少移动次数

如果无法从方格 (x1,y1) 移动至方格 (x2,y2),则输出 -1

数据范围

前 66 个测试点满足 1≤n,m≤10。
所有测试点满足 1≤n,m,k≤1000,1≤x1,x2≤n,1≤y1,y2≤m。

输入样例1:

3 4 4
....
###.
....
1 1 3 1

输出样例1:

3

输入样例2:

3 4 1
....
###.
....
1 1 3 1

输出样例2:

8

输入样例3:

2 2 1
.#
#.
1 1 2 2

输出样例3:

-1

 代码呈上

#include<iostream>
#include<cstring>
#include<queue>
#include<algorithm>//万能开头
using namespace std;
const int N=1010;
int n,m,k;
int ax,ay,bx,by;
char temp[N][N];
int dis[N][N];
int dx[4]={-1,1,0,0},dy[4]={0,0,-1,1};//差不多DFS必备了吧
int main()
{
    scanf("%d%d%d",&n,&m,&k);
    
    for(int i=1;i<=n;i++)//行
        scanf("%s",temp[i]+1);//因为我们每一次列都是从第一个开始存的所以加一
    
    scanf("%d%d%d%d",&ax,&ay,&bx,&by);
    memset(dis,-1,sizeof dis);//先初始化将所有其所有定义为-1,有的是要定义无穷,要分题而论
    dis[ax][ay]=0;//用过的赋值为0
    if(ax==bx&&ay==by)//如果起点和终点完全符合,直接输出
    {
        cout<<0;
        return 0;
    }
    queue<pair<int,int>> g;//这个很牛,DFS都要用,相当于吧就是可以同时同个编号存多个数的数组
    g.push({ax,ay});//这可不是用scanf,这个直接扔进去push进行,是以列表存储的
    
    while(g.size())//根据是否有队头来当作循环条件
    {
        auto a=g.front();//取出队头
        g.pop();//相当于把队头弹出去
        
        for(int i=0;i<4;i++)//循环四个方向(上,下,左,右)
        {
            for(int j=1;j<=k;j++)//这个题这里是1~k步要注意!
            {
                int x=a.first+j*dx[i],y=a.second+j*dy[i];//现在要判断的位置
                if(temp[x][y]=='#'||x<1||x>n||y<1||y>m)break;
                //第一层条件,如果是个陷阱,或者超出范围直接out!
                if(dis[x][y]!=-1&&dis[x][y]<=dis[a.first][a.second])break;
                //第二层条件,相当于剪枝,为了防止超时,如果本次的点之前已经到达过,
                //并且效果更好(走的步数更少),就直接out!因为其他这个点的四周肯定也是之前都判断过了
                if(dis[x][y]!=-1)continue;
                //第三层条件,要看题它是不影响的(注意:注意区分本题中移动次数与移动步数的区别。)
                
                g.push({x,y});//把这个点放入队头,如果多了也只会拿第一个
                dis[x][y]=dis[a.first][a.second]+1;
                //为什么每次都加1,其实它都是在起点的基础上,
                //并不是累加,在1~k都是一样的,为了防止在此期间就已经到达结尾
                if(x==bx&&y==by)//如果到达终点就输出次数
                {
                    cout<<dis[x][y];
                    return 0;
                }
            }
        }
    }
    puts("-1");
    // cout<<-1;
    return 0;
}

 

补充知识点:

auto的原理就是根据后面的值,来自己推测前面的类型是什么。

auto的作用就是为了简化变量初始化,如果这个变量有一个很长很长的初始化类型,就可以用auto代替。

错误原因:

1.本周还是要背一下DFS的板子,对于剪枝每次自己都想不到,就会tle,难啊难!

2.put("-1");笑不活了,还在想为啥为啥,突然想到没有一种可能是puts("-1"),这种可不敢出问题

3.今天看到了一个童鞋的博客说,y1与万能头(#include<bits/stdc++.h>)冲突,我没事,所以考试安安生生的多写点把/(ㄒoㄒ)/~~

欢迎来到一日一题的小菜鸟频道,睡不着就看看吧!

跟着小张刷题吧!
跟着小张刷题吧!

 

 

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
一 。课程设计目:漫步迷宫 二 。问描述: 用mn的m*n个正方格表示一个迷宫,其划有斜线的方格表示不可通,未划有斜线的方格表示可以通。请编写寻找从入口到出口的一条最短路径的程序。 三 。基本要求: 1。迷宫的规格(即数与数),状态设置(即各方格能否通的状态),以及入口和出口的位置,均应由输入随机确定。 2。求得的最短路径,应该以从入口到出口的路径上的各个方格的坐标的线性序输出。当无通路时,应该报告无路径的信息。 3。尽量采用结构化程序设计方法,要求对各个模块的功能及参数作必要的说明。 四 。实现提示: 1。迷宫可以采用matrix类型的二维数组A表示。A.rownum与A.colnum分别表示迷宫的实际的数与数。而A.maze[i][j]表示迷宫第i第j一个方格,用A.maze[i][j]=0表示该方格可以通,用A.maze[i][j]=1表示该方格不可以通。 2。由于要寻找从入口到出口的一条最短路径,最好将迷宫看作是一个图结构。则问转化为寻找从对应于入口顶点到对应于出口顶点的一条最短路径的问。该问可以采用从入口顶点出发,进广度优先搜索遍历,直到遇到出口顶点或者遍历完毕也没有遇到出口顶点为止。这二种情况分别对应于最短路径探索成功与查无通路的事实。 3。基于上述分析,涉及到数据结构的转换,即将二维数组表示的迷宫A转换为以adjlist 类型的邻接表表示的图结构G。在图结构,将迷宫的每个方格看作是一个顶点。不可通方格都是孤立顶点;相邻的可通方格所对应的顶点之间看作是有边相连。因此迷宫 可以看作是由m*n个顶点及无向边构成的一个非连通的无向图。尽管图是不连通的,但不影响本问的求解,而且本问有解的条件是:入口顶点与出口顶点在同一个连通分量。 图结构G,G.adj[k]表示编号为k的顶点的邻接情况的单链表的头指针;G.vexnum表示图G的实际顶点数,而且具有如下关系:G.vexnum=A.rownum*A.colnum 4。为了避免迷宫数据的重复输入,我们期望A能够自动地转换为G。因此应该设计一个转换算法create_adjlist(A,G)。而图结构顶点是要编号的,我们约定以为序,顺序给迷宫A方格所对应的顶点编号。这样迷宫方格的坐标(即row和col)与图G所对应的顶点的编号(即verno)之间具有如下关系: verno=(row-1)* n + col row=(verno-1)/ n + 1 col=(verno-1)% n + 1 5。在广度优先搜索遍历求解最短路径过程,应该设置一个queue作为辅助数据结构;路径采用一个整数数组pred来表示。这二个数据结构的存储结构类型均为list类型,其说明定义如下:typedef int list[MAXVER]; 队queue应该设置front和rear分别指示首与尾,queue[k]表示第k个入的顶点编号。采用pred记录路径,pred[i]表示顶点i在广度优先搜索遍历过程的前趋顶点的编号,它表明是经过边(pred[i],i)达到顶点i的。这样,当路径探索成功时,我们可以从出口顶点倒推出从入口到出口的一条路径来。当然要涉及到从顶点编号向方格坐标的反转换,这个公式在上面已经给出了。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值