题目:有一个n*m的棋盘(1<n,m<=400),在某个点上有一个马,要求你计算出马到达棋盘上任意一个点最少要走几步
很早的时候就知道这题是一道裸广搜,但因为我一直避重就轻的学习,所以一直没有学习广搜,昨天看到这道题的时候我就知道是时候把之前欠下的债都补回来了!
推荐文章:https://www.cnblogs.com/lichangjian/p/12369544.html 对对对这是lixx大佬写的,超级清晰,有兴趣的同学可以去看一下(是真的很清晰,反正我看这篇博客会了
然后这道题好像也没有什么特别坑的地方(除了输出),我就直接给你们讲一下AC代码把,不过坑我还是会说的嗯。
我因为输出WA了2次(1次0分,1次30分),因为数组开小了WA了4次(4次90分)
首先这是我们需要定义的变量以及数组,数组一定不能开小,我一开始开了1万以为已经够大了,结果RE了好多次:
int n,m,mx,my,book[410][410],a=0,b=0,la[410][410]; //book数组是用来标记的,因为广搜第一次遇到的一定是最小的步数,我们在他第一次遇到的时候就把他标记起来,就不会被覆盖一些不正确的值了。la在这里是用来存每个格子的步数的(以便于输出)。n,m,mx,my分别是需要输入的四个数。a和b分别是广搜中的头和尾。 int dx[10]={-1,1,-1,1,2,2,-2,-2},xx=0; //dx是当前这个点能到达的8个方向的x值(就是我们要每次都让当前这个点的x坐标加上这其中的一个数,当然y也得加同下标的y值,就可以得到一个新的,刚才那个点能到达的八个坐标中的一个了),xx表示的是每次加上dx值的x坐标。 int dy[10]={-2,-2,2,2,1,-1,1,-1},yy=0; //dy数组道理与上面的dy相同,yy与xx相同。 int x[100010],y[100010],bushu[10010]; //x是用来存x坐标值的,y是用来存y坐标的,bushu是用来存每一步的步数的,这三个数组是关键!!
下面是main函数里面的东西,输入我就不说了,反正大家都会嘛,我们直接来看广搜这一部分,我感觉注释已经尽量写的很详细了:
x[a]=mx;//这里可以说是在初始化,让起点的x和y都等于马所在的x和y y[a]=my; a++;//a是尾,添加了新元素我们就让尾加加 //是的我们就是在模拟队列,你直接用queue也可以啦,不过我更喜欢用数组模拟(主要是因为不会) while(b<a){//while如果头<尾那么就运行,也就是如果头>=尾就结束,表示这个广搜结束了搜完了(都搜完了你还搜那可就厉害了) for(int i=0;i<8;i++){//模拟八个方向 xx=x[b]+dx[i];//每次让xx和yy都分别加上dx和dy yy=y[b]+dy[i];//这里解释下为什么让x[b]和y[b]加dx、dy呢 //因为x[b]和y[b]是他们的根源,也可以说是他俩的爸爸,儿子肯定是在爸爸的基础上加的呀 if(xx>0&&xx<=n&&yy>0&&yy<=m&&book[xx][yy]==0){//判断这个新坐标是否越界以及是否判断过 book[xx][yy]=1;//如果没有被判断过,那么我们就标记一下,让他以后不在判断了,也就是说只判断这一次 //只判断一次得到的值一定是最小值 x[a]=xx;//更新x数组 y[a]=yy;//更新y数组 bushu[a]=bushu[b]+1;//步数也要更新啦,当然也要等于他爸爸的步数+1 la[xx][yy]=bushu[a];//这个数组la是用来输出的,会很方便 a++;//因为有了新的x和y我们也要让尾巴加个一啦 } } b++;//头每次+1,也就是每次都帮一个爸爸找儿子,一直找到找完为止 }
希望儿子爸爸这个比喻可以让你们看懂,李老师经常这样给我们讲课,很清晰的(๑•̀ㅂ•́)و✧
接下来是输出的问题,大家一定要注意看哈:
la[mx][my]=0;//让最开始马的坐标=0,因为马不需要走就在那个坐标上了,再标记一遍更保险点 for(int i=1;i<=n;i++){//for嵌套输出步数 for(int j=1;j<=m;j++){ if(la[i][j]==0){//如果当前的步数=0,表示这个坐标要么是马一开始的坐标,要么就是马去不了的地方 if(i==mx&&j==my) la[i][j]=0;//这一句其实没啥用啦,主要是我们需要用else //如果直接判断if(i!=mx&&j!=my)的话万一其中刚好有一个行数是i或者列数是j就会死得很惨 else la[i][j]=-1;//如果他既等于零而且又不是马一开始的坐标的话就说明这个马去不了的地方,题目要求去不了的地方输出-1 } printf("%-5d",la[i][j]);//%-5d是一个很实用的,查了很多资料才晓得,既可以左对齐又可以让他输出的场宽为5,完美 } printf(" ");//每输出满一行就回车 } return 0;//return 0不能忘
我第一次就错在场宽为5这上面了,第二次错在马一开始的坐标上面,我好南qwq
下面放一下完整代码啦:
#include<iostream> #include<cstdio> using namespace std; int n,m,mx,my,book[410][410],a=0,b=0,la[410][410]; int dx[10]={-1,1,-1,1,2,2,-2,-2},xx=0,bx=0; int dy[10]={-2,-2,2,2,1,-1,1,-1},yy=0,by=0; int x[100010],y[100010],bushu[100010]; int main(){ scanf("%d%d%d%d",&n,&m,&mx,&my); x[a]=mx;//这里可以说是在初始化,让起点的x和y都等于马所在的x和y y[a]=my; a++;//a是尾,添加了新元素我们就让尾加加 //是的我们就是在模拟队列,你直接用queue也可以啦,不过我更喜欢用数组模拟(主要是因为不会) while(b<a){//while如果头<尾那么就运行,也就是如果头>=尾就结束,表示这个广搜结束了搜完了(都搜完了你还搜那可就厉害了) for(int i=0;i<8;i++){//模拟八个方向 xx=x[b]+dx[i];//每次让xx和yy都分别加上dx和dy yy=y[b]+dy[i];//这里解释下为什么让x[b]和y[b]加dx、dy呢 //因为x[b]和y[b]是他们的根源,也可以说是他俩的爸爸,然后他如果要一个新的儿子的话,肯定是在爸爸的基础上加的 if(xx>0&&xx<=n&&yy>0&&yy<=m&&book[xx][yy]==0){//判断这个新坐标是否越界以及是否判断过 book[xx][yy]=1;//如果没有被判断过,那么我们就标记一下,让他以后不在判断了,也就是说只判断这一次 //只判断一次得到的值一定是最小值 x[a]=xx;//更新x数组 y[a]=yy;//更新y数组 bushu[a]=bushu[b]+1;//步数也要更新啦,当然也要等于他爸爸的步数+1 la[xx][yy]=bushu[a];//这个数组la是用来输出的,会很方便 a++;//因为有了新的x和y我们也要让尾巴加个一啦 } } b++;//头每次+1,也就是每次都帮一个爸爸找儿子,一直找到没爸爸为止 } la[mx][my]=0;//让最开始马的坐标=0,因为马不需要走就在那个坐标上了,再标记一遍更保险点 for(int i=1;i<=n;i++){//for嵌套输出步数 for(int j=1;j<=m;j++){ if(la[i][j]==0){//如果当前的步数=0,表示这个坐标要么是马一开始的坐标,要么就是马去不了的地方 if(i==mx&&j==my) la[i][j]=0;//这一句其实没啥用啦,主要是我们需要用else //如果直接判断if(i!=mx&&j!=my)的话万一其中刚好有一个行数是i或者列数是j就会死得很惨 else la[i][j]=-1;//如果他既等于零而且又不是马一开始的坐标的话就说明这个马去不了的地方,题目要求去不了的地方输出-1 } printf("%-5d",la[i][j]);//%-5d是一个很实用的,查了很多资料才晓得,既可以左对齐又可以让他输出的场宽为5,完美 } printf(" ");//每输出满一行就回车 } return 0;//return 0不能忘 }