目录
BFS算法:
思路:
1.从初始状态S开始,利用规则,生成下一层的状态。
2.顺序检查下一层的所有状态,看是否出现目标状态G。
否则就对该层所有状态节点,分别利用规则。生成再下一层的所有状态节点。
3.继续按照上面思想生成下一层的所有状态节点,这样一层一层往下展开。
直到出现目标状态为止
先遍历离起点近的,再到远的,网上有一个很形象的例子:你的眼镜掉在地上以后,你趴在地板上找。你总是先摸离你最近的地方,如果没有,再摸距离较远的地方。
通常用队列来实现
初始化队列Q
Q={起点s};
标记s为已访问;
while(Q非空)
{
取Q队首元素u;
u出队;
if(u==目标状态)
{.............}
所有与u相邻且未被访问的点进入队列;
标记u为已访问;
}
广度优先搜索算法如下:(用 QUEUE )
(1) 把初始节点 S0 放入 Open 表中;
(2) 如果 Open 表为空,则问题无解失败 退出;
(3) 把Open 表的第一个节点取出放入 Closed 表,并记该节点为 n;
(4) 考察节点 n是否为目标节点。若, 则得到问题的解,成功退出;
(5) 若节点 n不可扩展,则转第 (2) 步;
(6) 扩展节点 n,将其不在 Closed 表和 Open 表中的子节点 (判重) 放入 Open 表的尾部 ,并为每一个子节点设置指向父的针 (或记录节点的层次) ,然后转第 (2) 步
这个网址有BFS例子具体的实现过程:
POJ 3278 Catch That Cow
Description
Farmer John has been informed of the location of a fugitive cow and wants to catch her immediately. He starts at a point N (0 ≤ N ≤ 100,000) on a number line and the cow is at a point K (0 ≤ K ≤ 100,000) on the same number line. Farmer John has two modes of transportation: walking and teleporting.
* Walking: FJ can move from any point X to the points X - 1 or X + 1 in a single minute
* Teleporting: FJ can move from any point X to the point 2 × X in a single minute.
If the cow, unaware of its pursuit, does not move at all, how long does it take for Farmer John to retrieve it?
Input
Line 1: Two space-separated integers: N and K
Output
Line 1: The least amount of time, in minutes, it takes for Farmer John to catch the fugitive cow.
Sample Input
5 17
Sample Output
4
Hint
The fastest way for Farmer John to reach the fugitive cow is to move along the following path: 5-10-9-18-17, which takes 4 minutes.
Source
北大课件代码:
#include<stdio.h>
#include<stdlib.h>
#include<iostream>
#include<algorithm>
#include<string.h>
#include<queue>
using namespace std;
const int maxn=1e5;
int n,k;
int vis[maxn+10];
struct step
{
int x;
int steps;
step(int xx,int s):x(xx),steps(s){ }
};
queue<step>Q;
void bfs()
{
while(!Q.empty())
{
step s=Q.front();
if(s.x==k)
{
cout<<s.steps<<endl;
return ;
}
else
{
if(s.x-1>=0 && !vis[s.x-1])
{
Q.push(step(s.x-1,s.steps+1));
vis[s.x-1]=1;
}
if(s.x+1<=maxn && !vis[s.x+1])//步数小于maxn
{
Q.push(step(s.x+1,s.steps+1));
vis[s.x+1]=1;
}
if(s.x*2<=maxn && !vis[s.x*2])//步数小于maxn
{
Q.push(step(s.x*2,s.steps+1));
vis[s.x*2]=1;
}
Q.pop();
}
}
return ;
}
int main()
{
cin>>n>>k;
memset(vis,0,sizeof(vis));
Q.push(step(n,0));
vis[n]=1;
bfs();
return 0;
}
注意:
1.s.x+1<=maxn,s.x*2<=maxn。
自己之前代码:
#include<stdio.h>
#include<stdlib.h>
#include<iostream>
#include<algorithm>
#include<string.h>
#include<queue>
using namespace std;
const int maxn=200100;//注意这个地方的取值,maxn=1e5+100 POJ上交 Runtime Error
struct Node
{
int x;
int step;
};
int n,k;
int vis[maxn];
queue<Node>Q;
void bfs()
{
while(!Q.empty())
{
Node w2=Q.front();
Q.pop();
int x=w2.x;
int step=w2.step;
if(x==k)
{
cout<<step<<endl;
return ;
}
if(x<=k && !vis[x+1])
{
Node w3;
vis[x+1]=1;
w3.x=x+1;
w3.step=step+1;
Q.push(w3);
}
if(x>=1 && !vis[x-1])
{
Node w3;
vis[x-1]=1;
w3.x=x-1;
w3.step=step+1;
Q.push(w3);
}
if(x<=k && !vis[2*x])
{
Node w3;
vis[2*x]=1;
w3.x=x*2;
w3.step=step+1;
Q.push(w3);
}
}
}
int main()
{
while(scanf("%d%d",&n,&k)!=EOF)
{
while(!Q.empty())
Q.pop();
memset(vis,0,sizeof(vis));
vis[n]=1;
Node w1;
w1.x=n;
w1.step=0;
Q.push(w1);
bfs();
}
return 0;
}
百练 4116:拯救行动
描述
公主被恶人抓走,被关押在牢房的某个地方。牢房用N*M (N, M <= 200)的矩阵来表示。矩阵中的每项可以代表道路(@)、墙壁(#)、和守卫(x)。
英勇的骑士(r)决定孤身一人去拯救公主(a)。我们假设拯救成功的表示是“骑士到达了公主所在的位置”。由于在通往公主所在位置的道路中可能遇到守卫,骑士一旦遇到守卫,必须杀死守卫才能继续前进。
现假设骑士可以向上、下、左、右四个方向移动,每移动一个位置需要1个单位时间,杀死一个守卫需要花费额外的1个单位时间。同时假设骑士足够强壮,有能力杀死所有的守卫。
给定牢房矩阵,公主、骑士和守卫在矩阵中的位置,请你计算拯救行动成功需要花费最短时间。
输入
第一行为一个整数S,表示输入的数据的组数(多组输入)
随后有S组数据,每组数据按如下格式输入
1、两个整数代表N和M, (N, M <= 200).
2、随后N行,每行有M个字符。"@"代表道路,"a"代表公主,"r"代表骑士,"x"代表守卫, "#"代表墙壁。
输出
如果拯救行动成功,输出一个整数,表示行动的最短时间。
如果不可能成功,输出"Impossible"
样例输入
2
7 8
#@#####@
#@a#@@r@
#@@#x@@@
@@#@@#@#
#@@@##@@
@#@@@@@@
@@@@@@@@
13 40
@x@@##x@#x@x#xxxx##@#x@x@@#x#@#x#@@x@#@x
xx###x@x#@@##xx@@@#@x@@#x@xxx@@#x@#x@@x@
#@x#@x#x#@@##@@x#@xx#xxx@@x##@@@#@x@@x@x
@##x@@@x#xx#@@#xxxx#@@x@x@#@x@@@x@#@#x@#
@#xxxxx##@@x##x@xxx@@#x@x####@@@x#x##@#@
#xxx#@#x##xxxx@@#xx@@@x@xxx#@#xxx@x#####
#x@xxxx#@x@@@@##@x#xx#xxx@#xx#@#####x#@x
xx##@#@x##x##x#@x#@a#xx@##@#@##xx@#@@x@x
x#x#@x@#x#@##@xrx@x#xxxx@##x##xx#@#x@xx@
#x@@#@###x##x@x#@@#@@x@x@@xx@@@@##@@x@@x
x#xx@x###@xxx#@#x#@@###@#@##@x#@x@#@@#@@
#@#x@x#x#x###@x@@xxx####x@x##@x####xx#@x
#x#@x#x######@@#x@#xxxx#xx@@@#xx#x#####@
样例输出
13
7
一开始做这个题目的时候,觉得像 Catch That Cow,只不过多计算了步数而已,所以我就用了普通队列,按照上下左右四个方向走,是守卫步数就+2,是通道步数就+1,但是我忘记考虑了一个问题,在走有守卫和通道这两种选择上,应该选择步数最少了,所以应该用优先队列。.
自己代码:
#include<stdio.h>
#include<stdlib.h>
#include<iostream>
#include<algorithm>
#include<string.h>
#include<queue>
using namespace std;
const int maxn=250;
struct Node
{
int x;
int y;
int step;
bool operator<(const Node &n)const{
return step>n.step;
}
};
char g[maxn][maxn];
int n,m;
int u,v;
int vis[maxn][maxn];
int dir[4][2]={1,0,0,1,-1,0,0,-1};
priority_queue<Node>Q;
void bfs()
{
Node now,last;
while(!Q.empty())
{
last=Q.top();
Q.pop();
int x=last.x;
int y=last.y;
int step=last.step;
for(int i=0;i<4;i++)
{
now.x=x+dir[i][0];
now.y=y+dir[i][1];
if(now.x>=0 && now.x<n && now.y>=0 && now.y<m && !vis[now.x][now.y] && g[now.x][now.y]!='#')
{
if(g[now.x][now.y]=='x')
{
Node w1;
w1.x=now.x;
w1.y=now.y;
w1.step=step+2;
vis[now.x][now.y]=1;
Q.push(w1);
}
if(g[now.x][now.y]=='@')
{
Node w1;
w1.x=now.x;
w1.y=now.y;
w1.step=step+1;
vis[now.x][now.y]=1;
Q.push(w1);
}
if(g[now.x][now.y]=='a') //如果找到公主的位置,步数+1,然后输出即可
{
printf("%d\n",step+1);
return ;
}
}
}
}
cout<<"Impossible"<<endl;
}
int main()
{
int S;
while(scanf("%d",&S)!=EOF)
{
while(S--)
{
scanf("%d%d",&n,&m);
memset(vis,0,sizeof(vis));
while(!Q.empty())
Q.pop();
for(int i=0;i<n;i++)
{
scanf("%s",g[i]);
}
for(int i=0;i<n;i++)
{
for(int j=0;j<m;j++)
{
if(g[i][j]=='r')
{
Node t;
t.x=i;
t.y=j;
t.step=0;
vis[i][j]=1;
Q.push(t);
}
if(g[i][j]=='a')
{
u=i;
v=j;
}
}
}
bfs();
}
}
return 0;
}
错误代码:没有把公主的坐标放进队列
void bfs()
{
Node now,last;
while(!Q.empty())
{
last=Q.top();
Q.pop();
int x=last.x;
int y=last.y;
int step=last.step;
printf("x=%d y=%d\n",x,y);
if(x==u && y==v)//公主坐标不在队列中,所以永远不会执行这一句if语句,也就不会输出步数
{
printf("%d\n",step);
return ;
}
for(int i=0;i<4;i++)
{
now.x=x+dir[i][0];
now.y=y+dir[i][1];
if(now.x>=0 && now.x<n && now.y>=0 && now.y<m && !vis[now.x][now.y] && g[now.x][now.y]!='#')
{
if(g[now.x][now.y]=='x')
{
Node w1;
w1.x=now.x;
w1.y=now.y;
w1.step=step+2;
vis[now.x][now.y]=1;
Q.push(w1);
}
if(g[now.x][now.y]=='@')
{
Node w1;
w1.x=now.x;
w1.y=now.y;
w1.step=step+1;
vis[now.x][now.y]=1;
Q.push(w1);
}
}
}
}
cout<<"Impossible"<<endl;
}
注意:
1.一开始只判断了骑士走的是道路还是 守卫,并没有判断公主的位置,找到通道和守卫的位置,记录坐标和步数,放进队列,找到公主的位置也应该记录坐标的和步数,这样在另一层循环的时候取队头,取到公主的坐标,才能进入if语句输出步数。
课件代码:
#include<stdio.h>
#include<stdlib.h>
#include<iostream>
#include<algorithm>
#include<string.h>
#include<queue>
using namespace std;
const int maxn=250;
struct Node
{
int x;
int y;
int step;
bool operator<(const Node &f)const{//重载的小于运算符 在push/top中用到了
return step>f.step;//最小值优先
}
Node(int xx,int yy,int ss):x(xx),y(yy),step(ss){ }//给x,y,step不算赋值
};
char g[maxn][maxn];
int n,m;
int u,v;
int vis[maxn][maxn];
int dir[4][2]={1,0,0,1,-1,0,0,-1};
priority_queue<Node>Q;
void bfs()
{
while(!Q.empty())
{
int hx=Q.top().x;
int hy=Q.top().y;
int hs=Q.top().step;
Q.pop();
//vis[hx][hy]=1;
// printf("hx= %d hy=%d hs=%d\n",hx,hy,hs);
if(hx==u && hy==v)
{
printf("%d\n",hs);
return ;
}
for(int i=0;i<4;i++)
{
int nowx=hx+dir[i][0];
int nowy=hy+dir[i][1];
if(nowx>=0 && nowx<n && nowy>=0 && nowy<m && !vis[nowx][nowy] && g[nowx][nowy]!='#')
{
if(g[nowx][nowy]=='x')
{
Q.push(Node(nowx,nowy,hs+2));
}
else
{
Q.push(Node(nowx,nowy,hs+1));
}
vis[nowx][nowy]=1;
}
}
}
cout<<"Impossible"<<endl;
}
int main()
{
int S;
while(scanf("%d",&S)!=EOF)
{
while(S--)
{
memset(vis,0,sizeof(vis));
while(!Q.empty())
Q.pop();
scanf("%d%d",&n,&m);
for(int i=0;i<n;i++)
scanf("%s",g[i]);
for(int i=0;i<n;i++)
{
for(int j=0;j<m;j++)
{
if(g[i][j]=='r')
{
// printf("i=%d j=%d\n",i,j);
vis[i][j]=1;
Q.push(Node(i,j,0));
}
if(g[i][j]=='a')
{
//printf("u=%d v=%d\n",u,v);
u=i;
v=j;
}
}
}
bfs();
}
}
return 0;
}
注意:
1.g[i][j]=='a', 两个等于号,不要漏掉。
2.使用优先队列。
4115:鸣人和佐助
描述
佐助被大蛇丸诱骗走了,鸣人在多少时间内能追上他呢?
已知一张地图(以二维矩阵的形式表示)以及佐助和鸣人的位置。地图上的每个位置都可以走到,只不过有些位置上有大蛇丸的手下,需要先打败大蛇丸的手下才能到这些位置。鸣人有一定数量的查克拉,每一个单位的查克拉可以打败一个大蛇丸的手下。假设鸣人可以往上下左右四个方向移动,每移动一个距离需要花费1个单位时间,打败大蛇丸的手下不需要时间。如果鸣人查克拉消耗完了,则只可以走到没有大蛇丸手下的位置,不可以再移动到有大蛇丸手下的位置。佐助在此期间不移动,大蛇丸的手下也不移动。请问,鸣人要追上佐助最少需要花费多少时间?
输入
输入的第一行包含三个整数:M,N,T。代表M行N列的地图和鸣人初始的查克拉数量T。0 < M,N < 200,0 ≤ T < 10
后面是M行N列的地图,其中@代表鸣人,+代表佐助。*代表通路,#代表大蛇丸的手下。
输出
输出包含一个整数R,代表鸣人追上佐助最少需要花费的时间。如果鸣人无法追上佐助,则输出-1。
样例输入
样例输入1
4 4 1
#@##
**##
###+
****
样例输入2
4 4 2
#@##
**##
###+
****
样例输出
样例输出1
6
样例输出2
4
多一个判断查克拉的数组,因此要多一步
问题代码: 两种方式均没有记录查克拉。
/*
#include<stdio.h>
#include<stdlib.h>
#include<iostream>
#include<algorithm>
#include<string.h>
#include<queue>
using namespace std;
const int maxn=250;
int n,m,t;
int u,v;
struct Node
{
int x;
int y;
int minute;
int ckl;
Node(int xx,int yy,int mm,int cc): x(xx),y(yy),minute(mm),ckl(cc){ }
};
char g[maxn][maxn];
int vis[maxn][maxn];
int dir[4][2]={1,0,0,1,-1,0,0,-1};
queue<Node>Q;
void bfs()
{
while(!Q.empty())
{
int hx=Q.front().x;
int hy=Q.front().y;
int hm=Q.front().minute;
int hc=Q.front().ckl;
if(hx==u && hy==v)
{
printf("%d\n",hm);
return ;
}
for(int i=0;i<4;i++)
{
int nx=hx+dir[i][0];
int ny=hy+dir[i][1];
if(nx>=0 && nx<m&&ny>=0&& ny<n&&!vis[nx][ny])
{
if(g[nx][ny]=='*')
{
Q.push(nx,ny,hm+1,hc);
}
if(g[nx][ny]=='#'&&hc>0)
{
Q.push(nx,ny,hm+1,hc-1);
}
if(g[nx][ny]=='+')
{
Q.push(nx,ny,hm+1,hc)
}
}
}
}
puts("-1");
}
int main()
{
scanf("%d%d%d",&m,&n,&t);
memset(vis,0,sizeof(vis));
while(!Q.empty())
Q.pop();
for(int i=0;i<m;i++)
{
scanf("%s",g[i]);
}
for(int i=0;i<m;i++)
{
for(int j=0;j<n;j++)
{
if(g[i][j]=='@')
{
// printf("i=%d j=%d\n",i,j);
vis[i][j]=1;
Q.push(Node(i,j,0,t));
}
if(g[i][j]=='+')
{
u=i;
v=j;
// printf("u=%d v=%d\n",u,v);
}
}
}
bfs();
return 0;
}
*/
#include<stdio.h>
#include<stdlib.h>
#include<iostream>
#include<algorithm>
#include<string.h>
#include<queue>
using namespace std;
const int maxn=250;
int n,m,t;
int u,v;
struct Node
{
int x;
int y;
int minute;
int ckl;
};
char g[maxn][maxn];
int vis[maxn][maxn][maxn];
int dir[4][2]={1,0,0,1,-1,0,0,-1};
queue<Node>Q;
void bfs()
{
Node now,last;
while(!Q.empty())
{
last=Q.front();
Q.pop();
int hx=last.x;
int hy=last.y;
int hm=last.minute;
int ht=last.ckl;
if(hx==u && hy==v)
{
printf("%d\n",hm);
return ;
}
for(int i=0;i<4;i++)
{
now.x=hx+dir[i][0];
now.y=hy+dir[i][1];
if(now.x>=0 && now.x<m && now.y>=0 && now.y<n)
{
if(g[now.x][now.y]=='#' && !vis[now.x][now.y][now.ckl] && now.ckl>0)
{
Node w1;
w1.x=now.x;
w1.y=now.y;
w1.minute=now.minute+1;
w1.ckl=now.ckl-1;
Q.push(w1);
vis[now.x][now.y][now.ckl]=1;
}
if(g[now.x][now.y]!='#' && !vis[now.x][now.y][now.ckl] )
{
Node w1;
w1.x=now.x;
w1.y=now.y;
w1.minute=now.minute+1;
w1.ckl=now.ckl;
Q.push(w1);
vis[now.x][now.y][now.ckl]=1;
}
}
}
}
puts("-1");
}
int main()
{
scanf("%d%d%d",&m,&n,&t);
memset(vis,0,sizeof(vis));
while(!Q.empty())
Q.pop();
for(int i=0;i<m;i++)
{
scanf("%s",g[i]);
}
for(int i=0;i<m;i++)
{
for(int j=0;j<n;j++)
{
if(g[i][j]=='@')
{
Node w;
w.x=i;
w.y=j;
w.minute=0;
w.ckl=t;
vis[i][j][t]=1;
Q.push(w);
}
if(g[i][j]=='+')
{
u=i;
v=j;
}
}
}
bfs();
return 0;
}
注意:
1.二维数组的读入方式。不确定可以先输出看看是否正确。