概念:宽度优先搜索(又称广度优先搜索)是最简便的图的搜索算法之一,这一算法也是很多重要的图的算法的原型。Dijkstra算法和Prim算法都采用了和宽度优先搜索类似的思想。BFS属于一种盲目搜寻法(所以才叫搜索啊),用队列实现,目的是系统地展开并检查图中的所有节点,以找寻结果。(不要怪我扯上图论)换句话说,它并不考虑结果的可能位置,彻底地搜索整张图,直到找到结果为止。
相信对BFS大家都不陌生,下面直接看题:
T1.
有一种很奇怪的电梯。大楼的每一层楼都可以停电梯,而且第i层楼(1<=i<=N)上有一个数字Ki(0<=Ki<=N)(N<100)。电梯只有四个按钮:开,关,上,下。上下的层数等于当前楼层上的那个数字。当然,如果不能满足要求,相应的按钮就会失灵。例如:3 3 1 2 5代表了Ki(K1=3,K2=3,……),从一楼开始。在一楼,按“上”可以到4楼,按“下”是不起作用的,因为没有-2楼。那么,从A楼到B楼至少要按几次按钮呢?若无法到达就输出-1
样例输入:
5 1 5
3 3 1 2 5
样例输出:
3
这是一道很经典的题目,但为什么要用BFS做呢?(当然IDA*也是可以的)
思考:如果用DFS,可能会发生什么?
极有可能永远搜不到结果导致TLE或RE
至于实现就很简单了:
#include
using namespace std;
int n,m=-1,k,st,en,tmp,a[201]={},c[201]={};
bool b[201]={};
queue<int>q;
int bfs(){
tmp=st;b[tmp]=1;c[tmp]=0;
q.push(tmp);
do{
k=q.front();q.pop();
if((k-a[k]>0)&&(!b[k-a[k]])){
b[k-a[k]]=1;
q.push(k-a[k]);
c[k-a[k]]=c[k]+1;
}
if((k+a[k]<=n)&&(!b[k+a[k]])){
b[k+a[k]]=1;
q.push(k+a[k]);
c[k+a[k]]=c[k]+1;
}
if(b[en])return c[en];
}while(!q.empty());
return -1;
}
int main(){
memset(a,0,sizeof(a));
memset(b,0,sizeof(b));
memset(c,0,sizeof(c));
cin>>n>>st>>en;
for(int i=1;i<=n;i++)cin>>a[i];
m=bfs();cout<<m;
return 0;
}
是不是觉得太简单了,还有一题:
T2.
Farmer John为了满足宁智贤对美的享受而安装了人工湖。矩形的人工湖分成M行N列(1 <= M<= 30; 1 <= N <= 30)的方形小格子。有些格子有美丽的荷叶,有些有岩石,剩下的格子有的只是美丽的蓝色湖水。宁智贤通过从一片荷叶跳到另一片荷叶上来练习芭蕾。它现在正站在一片荷叶上(看输入数据了解具体位置)。它希望通过在荷叶上跳跃来到达另一片荷叶。它既不能跳到水里也不能跳到岩石上。只有新手才会感到吃惊:宁智贤的跳跃有点类似国际象棋中马那样的移动,在一个方向上(如水平方向)移动M1(1 <=M1 <= 30)“格”,然后再在另一个方向上(如竖直方向)移动M2 (1 <= M2 <=30; M1 != M2)格,所以宁智贤有时可能有多达8中的跳跃选择。
给出池塘的构造以及宁智贤跳跃的形式,找出宁智贤从一个位置移动到另一个位置所需的最小的跳跃次数。(保证有解)
输入格式:
第i+1行用N个空格分开的整数描述池塘第i行,0表示水,1表示荷叶,2表示岩石,3表示宁智贤现在站的那块荷叶,4表示跳跃的终点。
样例输入:
4 5 1 2
1 0 1 0 1
3 0 2 0 4
0 1 2 0 0
0 0 0 1 0
样例输出:
2
这道题又为什么要用BFS做呢?
当数据较小时,用DFS完全没有问题,但是数据如果较大,因为每一层会有8种情况,会TLE。同时,也极有可能出现找不到解的情况,所以只能用BFS解决。(IDA*。。应该还是可以的)。
思路确定,实现还是简单的:
#include
using namespace std;
const int maxn=40;
struct node{
int x,y,step; //分别为行、列、步数
}start,end,temp,tmp;
int n,m,ans;
bool ma[maxn][maxn],vis[maxn][maxn]={0};
int dx[8],dy[8];
int bfs()
{
queue<node>q; //队列,应该知道吧!
temp.x=start.x;temp.y=start.y;temp.step=0;
vis[temp.x][temp.y]=1;
q.push(temp);
while(!q.empty()) //如果队不为空
{
temp=q.front(); //队首
q.pop();
for(int i=0;i<8;i++) //八个方向逐个寻找
{
tmp=temp;
tmp.step++;tmp.x+=dx[i];tmp.y+=dy[i];
if(tmp.x==end.x && tmp.y==end.y) //如果到终点
return tmp.step;
if((tmp.x<1)||(tmp.y<1)||(tmp.x>n)||(tmp.y>m)||(ma[tmp.x][tmp.y]==1)||(vis[tmp.x][tmp.y]==1))
continue;
else {
vis[tmp.x][tmp.y]=1;q.push(tmp); //标记,入队
}
}
}
}
int main()
{
int m1,m2,a;
cin>>n>>m>>m1>>m2;
dx[0]=-m1;dy[0]=m2; dx[1]=-m1;dy[1]=-m2;
dx[2]=-m2;dy[2]=-m1;dx[3]=-m2;dy[3]=m1;
dx[4]=m1;dy[4]=m2; dx[5]=m1;dy[5]=-m2;
dx[6]=m2;dy[6]=m1; dx[7]=m2;dy[7]=-m1; //方向数组赋值
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++){
cin>>a;
if(a==0 || a==2) ma[i][j]=1; //1为水或岩石
else ma[i][j]=0;
if(a==3){
start.x=i;start.y=j;
}
if(a==4){
end.x=i;end.y=j;
}
}
ans=bfs();
cout<<ans<<endl;
return 0;
}
———————————————————正经分割线————————————————————
上面给出了两道例题,先看第一道:
观察数据范围:N<100。
再思考题目:对于一个点i,可以去到i-ki和i+ki这两个点(如果这两个点都在范围内的话)。
把这些点转化到图上,即第i个点与i-ki和i+ki连一条权值为1的有向边,那么就把问题转化成由点a去到点b的最短路了。
因为数据量小,用floyd就可以轻松解决:
#include
#pragma G++ optimize(2)
using namespace std;
int f[500][500];
int n,k,m;
int main()
{
memset(f,10,sizeof(f));
scanf("%d%d%d",&n,&k,&m);
for(int i=1;i<=n;i++)
{
int x;
scanf("%d",&x);
if (i-x>=1) f[i][i-x]=1;
if (i+x<=n) f[i][i+x]=1;
f[i][i]=0;
}
for(int k=1;k<=n;k++)
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
if (f[i][k]+f[k][j]<f[i][j]) f[i][j]=f[i][k]+f[k][j];
if (f[k][m]<=10000000) printf("%d",f[k][m]);
else printf("-1");
return 0;
}
是不是很惊讶,再观察T2:
数据范围:1≤m≤30,1≤n≤30,即最多有900个点
对于每个点,可能有八条连边(注意在这里连的是无向边,权值仍为1,利用这个可以有一些小优化)
仔细分析后,可以发现,用if和continue优化常数,floyed依然可以轻松过这道题,下面看代码:
#include
#pragma G++ optimize(2)
using namespace std;
int f[1000][1000],b[100][100]={},a[100][100]={};
int n,m,l,r,x,y,xx,yy;
int main()
{
memset(f,10,sizeof(f));
scanf("%d%d%d%d",&n,&m,&l,&r);
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
b[i][j]=(i-1)*m+j;
}
}
for(int i=1;i<=n*m;i++)
f[i][i]=0;
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
scanf("%d",&a[i][j]);
if (a[i][j]==4)
{
x=i;
y=j;
continue;
}
if (a[i][j]==3)
{
xx=i;
yy=j;
continue;
}
if (a[i][j]==2)
a[i][j]=0;
}
}
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
if (a[i][j]==0) continue;
if (i+r<=n&&j-l>=1)
if (a[i+r][j-l]!=0) f[b[i][j]][b[i+r][j-l]]=1,f[b[i+r][j-l]][b[i][j]]=1;
if (i+r<=n&&j+l<=m)
if (a[i+r][j+l]!=0) f[b[i][j]][b[i+r][j+l]]=1,f[b[i+r][j+l]][b[i][j]]=1;
if (i+l<=n&&j-r>=1)
if (a[i+l][j-r]!=0) f[b[i][j]][b[i+l][j-r]]=1,f[b[i+l][j-r]][b[i][j]]=1;
if (i+l<=n&&j+r<=m)
if (a[i+l][j+r]!=0) f[b[i][j]][b[i+l][j+r]]=1,f[b[i+l][j+r]][b[i][j]]=1;
}
}
for(int k=1;k<=n*m;k++)
for(int i=1;i<=n*m;i++)
for(int j=1;j<=n*m;j++)
if (f[i][k]+f[k][j]<f[i][j])
{
f[i][j]=f[i][k]+f[k][j];
}
if (f[(xx-1)*m+yy][(x-1)*m+y]<168430090) printf("%d\n",f[(xx-1)*m+yy][(x-1)*m+y]);
return 0;
}
是不是很有趣呢
————————————————————完——————————————————————