题目:有一个n*m的棋盘(1<n,m<=400),在某个点上有一个马,要求你计算出马到达棋盘上任意一个点最少要走几步html
很早的时候就知道这题是一道裸广搜,但由于我一直拈轻怕重的学习,因此一直没有学习广搜,昨天看到这道题的时候我就知道是时候把以前欠下的债都补回来了!ios
推荐文章:http://www.javashuo.com/article/p-rnbscagb-c.html 对对对这是lixx大佬写的,超级清晰,有兴趣的同窗能够去看一下(是真的很清晰,反正我看这篇博客会了
数组
而后这道题好像也没有什么特别坑的地方(除了输出),我就直接给大家讲一下AC代码把,不过坑我仍是会说的嗯。函数
我由于输出WA了2次(1次0分,1次30分),由于数组开小了WA了4次(4次90分)学习
首先这是咱们须要定义的变量以及数组,数组必定不能开小,我一开始开了1万觉得已经够大了,结果RE了好屡次:spa
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函数里面的东西,输入我就不说了,反正你们都会嘛,咱们直接来看广搜这一部分,我感受注释已经尽可能写的很详细了:code
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,也就是每次都帮一个爸爸找儿子,一直找到找完为止 }
但愿儿子爸爸这个比喻可让大家看懂,李老师常常这样给咱们讲课,很清晰的(๑•̀ㅂ•́)و✧htm
接下来是输出的问题,你们必定要注意看哈:blog
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("\n");//每输出满一行就回车 } 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("\n");//每输出满一行就回车 } return 0;//return 0不能忘 }