P1443 马的便利(BFS)笔记
万恶之源
马的遍历
解法
这道题几乎就是一个裸的BFS,以马为起点遍历宽搜整个棋盘即可,层数即为到某点的最短步数,最后输出即可。
若任意两个状态之间转移的代价都相同,那么BFS第一次访问到目标状态时,就是从起始状态到目标状态的最小代价。此题恰好移动代价都相同,如果不相同需要优先队列优化。
额外注意的点
- 如果使用getid(int x,int y)函数(一个自己定义将二维点压为一维点的函数),需要注意其中x的系数不能小于数据范围(400),否则会出现重复
int getid(int x,int y){
return x*500+y-1;
//解压只需要x=id/n,y=id%n+1
}
- 做这类类似用邻接矩阵存图的题一定要注意判断边界(包括马的坐标!!!),以及辨析长和宽。
bool check(int x,int y){
if (x<1 || y<1) return false;
if (x>n || y>m) return false;//这里不能是 x>m || y>n,辨析好变量的概念,养成好习惯,m是宽,n是长;
return true;
}
- 若题目的输出有宽度限制,不能简单的在输出数字后输出空格,应用限定符限制其宽度,c++的话,参考C++的cout高阶格式化操作
printf: %md (右对齐)
printf: %-md(左对齐)
cout:cout<<left<<setw(m)<<"输出的东西"<<endl;(左对齐)
cout<<right<<setw(m)<<"输出的东西"<<endl;(右对齐)
正题 BFS
BFS框架
queue<int> Q;
void BFS(int st) {
Q.push(st);
//接下来一系列初始化,图中往往要dis[st]=0以及memset
while(Q.empty()==0) { //队列不为空就重复执行
//某些预处理
for (int i = 0; i < n; i++) { //枚举子节点
int now = Q.front();
Q.pop();
//对子节点进行操作
if (/*如果子节点满足某些所需条件*/){
//某些更新操作
Q.push(i); //更新队列
}
}
}
}
BFS的特点
-
BFS有一个非常有用的特点,即逐层扩展的特点
-
若任意两个状态之间转移的代价都相同,那么BFS第一次访问到目标状态时,就是从起始状态到目标状态的最小代价
-
因为当把某一层的点全部遍历完后才会遍历到下一层的点(每次进队放在队的最末尾)
例如在网格图中求单源最短路,只需要从源点开始逐层扩展,d[i][j]表示从起点到点(i,j)的最小代价即可
需要注意的是
只有当状态两两之间相互转移的代价相同时,才能以第一次遍历到目标状态的代价作为最小代价
如果转移代价不相同,只有两条路可以走
使用优先队列,每次从队里拿出的状态不再是队头的状态,而是代价最小的
这就是堆优化的Dijkstra
允许反复进队,即某个状态第二次被访问到的时候,若此时代价更小则更新此状态的代价并再次进队,以更细其他状态,这种状态遇到好的数据,普通队列RE,优先队列TLE