# BFS的理解和应用

● BFS是方向发散的找，就是1变4，4变16这个意思，类似于分身，不同的分身还可以继续分身。

● 案例解释：找眼镜

你的眼镜掉在地上以后，你趴在地上找，你总是先摸到最接近你的地方，如果没有找到，再往远一点的地方找找。

● BFS包括单向BFS，双向BFS....

▶▶▶▶ DBFS算法是对BFS算法的一种扩展。对于BFS算法来说，DBFS算法由于采用了从两个点开始扩展的方式，搜索树的深度得到了明显的减少，所以在算法的时间复杂度和空间复杂度上都有较大的优势。另外，因为双向遍历也会出现多种相遇的情况，这时不能直接输出，而是应该检查完该层的所有节点以得到最优值，即交替逐层搜索。也即如果这层搜索遇到了另一个已经访问过的。那么已经搜索过的层数就是答案了，可以跳出来了，以后不会出现更优的了。而当某一边队列空时就无解了。

Knight Moves  HDOJ - 1372

● BFS

● DBFS

Rescue HDOJ - 1242

## Knight Moves  HDOJ - 1372

A friend of you is doing research on the Traveling Knight Problem (TKP) where you are to find the shortest closed tour of knight moves that visits each square of a given set of n squares on a chessboard exactly once. He thinks that the most difficult part of the problem is determining the smallest number of knight moves between two given squares and that, once you have accomplished this, finding the tour would be easy.
Of course you know that it is vice versa. So you offer him to write a program that solves the "difficult" part.

Your job is to write a program that takes two squares a and b as input and then determines the number of knight moves on a shortest route from a to b.

InputThe input file will contain one or more test cases. Each test case consists of one line containing two squares separated by one space. A square is a string consisting of a letter (a-h) representing the column and a digit (1-8) representing the row on the chessboard.
OutputFor each test case, print one line saying "To get from xx to yy takes n knight moves.".

Sample Input

e2 e4
a1 b2
b2 c3
a1 h8
a1 h7
h8 a1
b1 c3
f6 f6

Sample Output

To get from e2 to e4 takes 2 knight moves.
To get from a1 to b2 takes 4 knight moves.
To get from b2 to c3 takes 2 knight moves.
To get from a1 to h8 takes 6 knight moves.
To get from a1 to h7 takes 5 knight moves.
To get from h8 to a1 takes 6 knight moves.
To get from b1 to c3 takes 1 knight moves.
To get from f6 to f6 takes 0 knight moves.

### ● BFS

​
///BFS--Knight Moves HDOJ 1372
#include <stdio.h>
#include <iostream>
#include <cmath>
#include <cstring>
#include <queue>
#define MAX 100010
using namespace std;

struct position
{
int x;
int y;
};
///eight different terminus
position dir[8]={{-2,1},{-2,-1},{-1,-2},{1,-2},{2,-1},{2,1},{1,2},{-1,2}};
char c[6];///input coordinate
int a[6];///convert to number
queue<int> que;///用队列存
int vis[9][9];///chessboard
int ans;

int is_right(int a,int b)
{
if(a>0&&a<=8&&b>0&&b<=8)
return 1;
return 0;
}

int BFS()
{
int col,row,i;
ans=0;
que.push(a[0]);
que.push(a[1]);
que.push(ans);
vis[a[1]][a[0]]=1;
while(!que.empty())
{
col=que.front();    que.pop();
row=que.front();    que.pop();
ans=que.front();    que.pop();
if(col==a[2]&&row==a[3])///arrive
return ans;
for(i=0;i<8;i++)
{
if(is_right(row+dir[i].x,col+dir[i].y)&&
!vis[row+dir[i].x][col+dir[i].y])
{
que.push(col+dir[i].y);
que.push(row+dir[i].x);
que.push(ans+1);
vis[row+dir[i].x][col+dir[i].y]=1;
}
}
}
}
int main(void)
{
while(gets(c))
{
while(!que.empty())
que.pop();
memset(vis,0,sizeof(vis));
a[0]=c[0]-'a'+1;
a[1]=c[1]-'0';
a[2]=c[3]-'a'+1;
a[3]=c[4]-'0';
ans=BFS();
printf("To get from %c%c to %c%c takes %d knight moves.\n",c[0],c[1],c[3],c[4],ans);
}
return 0;
}

​

### ● DBFS

​
///DBFS -- Knight Moves HDOJ 1372
#include <stdio.h>
#include <iostream>
#include <cmath>
#include <cstring>
#include <queue>
#define MAX 100010
using namespace std;

struct position
{
int x;
int y;
};
///eight different terminus
position dir[8]={{-2,1},{-2,-1},{-1,-2},{1,-2},{2,-1},{2,1},{1,2},{-1,2}};
char c[6];///input coordinate
int a[6];///convert to number
queue<int> que1,que2;///定义两个队列
int vis[9][9];///chessboard
int cnt,ans,ans1,ans2;
bool flag;

int is_right(int a,int b)
{
if(a>0&&a<=8&&b>0&&b<=8)
return 1;
return 0;
}

{
int col,row,step,i;
col=que1.front();    que1.pop();
row=que1.front();    que1.pop();
step=que1.front();    que1.pop();
for(i=0;i<8;i++)
{
if(is_right(row+dir[i].x,col+dir[i].y)&&
vis[row+dir[i].x][col+dir[i].y]!=1)
{
ans1=step+1;
if(vis[row+dir[i].x][col+dir[i].y]==2)///found
{
flag=false;
ans=ans1+ans2;
return;
}
que1.push(col+dir[i].y);
que1.push(row+dir[i].x);
que1.push(ans1);
vis[row+dir[i].x][col+dir[i].y]=1;
}
}
}
void From_Tail_BFS()
{
int col,row,step,i;
col=que2.front();    que2.pop();
row=que2.front();    que2.pop();
step=que2.front();    que2.pop();
for(i=0;i<8;i++)
{
if(is_right(row+dir[i].x,col+dir[i].y)&&
vis[row+dir[i].x][col+dir[i].y]!=2)
{
ans2=step+1;
if(vis[row+dir[i].x][col+dir[i].y]==1)///found
{
flag=false;
ans=ans1+ans2;
return;
}
que2.push(col+dir[i].y);
que2.push(row+dir[i].x);
que2.push(ans2);
vis[row+dir[i].x][col+dir[i].y]=2;
}
}
}
int main(void)
{
while(gets(c))
{
while(!que1.empty())
que1.pop();
while(!que2.empty())
que2.pop();
memset(vis,0,sizeof(vis));
a[0]=c[0]-'a'+1;
a[1]=c[1]-'0';
a[2]=c[3]-'a'+1;
a[3]=c[4]-'0';
flag=true;
ans=ans1=ans2=0;
int step=0;
que1.push(a[0]);
que1.push(a[1]);
que1.push(step);
vis[a[1]][a[0]]=1;

que2.push(a[2]);
que2.push(a[3]);
que2.push(step);
vis[a[3]][a[2]]=2;

if(a[0]==a[2] && a[1]==a[3])///起点终点相等，直接输出
printf("To get from %c%c to %c%c takes 0 knight moves.\n",c[0],c[1],c[3],c[4]);
else
{
while(flag)
{
cnt=que1.size()/3;///统计本层结点个数，下面循环实现单层正向BFS扩展
while(cnt-- && flag)
cnt=que2.size()/3;///统计本层结点个数，下面循环实现单层反向BFS扩展
while(cnt-- && flag)
From_Tail_BFS();
}
printf("To get from %c%c to %c%c takes %d knight moves.\n",c[0],c[1],c[3],c[4],ans);
}
}
return 0;
}

​

## 逃离迷宫 HDOJ - 1728

Problem Description

给定一个m × n (m行, n列)的迷宫，迷宫中有两个位置，gloria想从迷宫的一个位置走到另外一个位置，当然迷宫中有些地方是空地，gloria可以穿越，有些地方是障碍，她必须绕行，从迷宫的一个位置，只能走到与它相邻的4个位置中,当然在行走过程中，gloria不能走到迷宫外面去。令人头痛的是，gloria是个没什么方向感的人，因此，她在行走过程中，不能转太多弯了，否则她会晕倒的。我们假定给定的两个位置都是空地，初始时，gloria所面向的方向未定，她可以选择4个方向的任何一个出发，而不算成一次转弯。gloria能从一个位置走到另外一个位置吗？

Input

第1行为一个整数t (1 ≤ t ≤ 100),表示测试数据的个数，接下来为t组测试数据，每组测试数据中，
第1行为两个整数m, n (1 ≤ m, n ≤ 100),分别表示迷宫的行数和列数，接下来m行，每行包括n个字符，其中字符'.'表示该位置为空地，字符'*'表示该位置为障碍，输入数据中只有这两种字符，每组测试数据的最后一行为5个整数k, x1, y1, x2, y2 (1 ≤ k ≤ 10, 1 ≤ x1, x2 ≤ n, 1 ≤ y1, y2 ≤ m),其中k表示gloria最多能转的弯数，(x1, y1), (x2, y2)表示两个位置，其中x1，x2对应列，y1, y2对应行。

Output

每组测试数据对应为一行，若gloria能从一个位置走到另外一个位置，输出“yes”，否则输出“no”。

Sample Input

2 5 5

...**

*.**.

.....

.....

*....

1 1 1 1 3

5 5

...**

*.**.

.....

.....

*....

2 1 1 1 3

Sample Output

no

yes

​///逃离迷宫 HDOJ - 1728
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <queue>
#include <cstring>
using namespace std;
///BFS
struct node
{
int x,y;
};
char map[209][209];
int used[209][209];
int dir[4][2]={0,1, 0,-1, 1,0, -1,0};
int n,m;
int k,sx,sy,ex,ey;
int judge(int x,int y)
{
return x>=0&&x<n && y>=0&&y<m && map[x][y]!='*';
}
void BFS(int x,int y)
{
queue<node> q;
node now,next;
now.x=x;
now.y=y;
q.push(now);
bool flag = false;
while(!q.empty())
{
now=q.front();
q.pop();
if(now.x==ex && now.y==ey)
{
if(used[ex][ey] <= k)
{
flag=true;
printf("yes\n");
}
else
break;
}
else
{
for(int i=0;i<4;i++)
{
next.x = now.x + dir[i][0];
next.y = now.y + dir[i][1];
while(judge(next.x,next.y))
{
if(used[next.x][next.y] == -1)
{
used[next.x][next.y] = used[now.x][now.y] + 1;
q.push(next);
}
next.x += dir[i][0];
next.y += dir[i][1];
}
}
}
}
if(!flag)
printf("no\n");
}
int main()
{
int t;
cin>>t;
while(t--)
{
cin>>n>>m;
for(int i=0;i<n;i++)
scanf("%s",map[i]);
scanf("%d%d%d%d%d",&k,&sy,&sx,&ey,&ex);
sx--,sy--,ex--,ey--;
memset(used,-1,sizeof(used));
BFS(sx,sy);
}
return 0;
}

​

## Rescue HDOJ - 1242

Problem Description

Angel was caught by the MOLIGPY! He was put in prison by Moligpy. The prison is described as a N * M (N, M <= 200) matrix. There are WALLs, ROADs, and GUARDs in the prison.

Angel's friends want to save Angel. Their task is: approach Angel. We assume that "approach Angel" is to get to the position where Angel stays. When there's a guard in the grid, we must kill him (or her?) to move into the grid. We assume that we moving up, down, right, left takes us 1 unit time, and killing a guard takes 1 unit time, too. And we are strong enough to kill all the guards.

You have to calculate the minimal time to approach Angel. (We can move only UP, DOWN, LEFT and RIGHT, to the neighbor grid within bound, of course.)

Input

First line contains two integers stand for N and M.
Then N lines follows, every line has M characters. "." stands for road, "a" stands for Angel, and "r" stands for each of Angel's friend.
Process to the end of the file.

Output

For each test case, your program should output a single integer, standing for the minimal time needed. If such a number does no exist, you should output a line containing "Poor ANGEL has to stay in the prison all his life."

Sample Input

7 8
#.#####.
#.a#..r.
#..#x...
..#..#.#
#...##..
.#......
........

Sample Output

13

​
///BFS Rescue HDOJ - 1242
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <queue>
#include <cstring>
using namespace std;
char map[209][209];
int dir[4][2]={1,0,-1,0,0,-1,0,1};
int n,m;
struct node
{
int x,y,step;
friend bool operator<(node a,node b)
{
return a.step>b.step;///重载运算符，步数少的排前面
}
};
int BFS(int x,int y)
{
priority_queue<node>q;
node cur,next;
int i,dx,dy;
map[x][y]='#';
cur.x=x;
cur.y=y;
cur.step=0;
q.push(cur);
while(!q.empty())
{
cur=q.top();
q.pop();
for(i=0;i<4;i++)///四方扩散，广度搜索
{
dx=cur.x+dir[i][0];
dy=cur.y+dir[i][1];
if(dx>=0&&dx<n&&dy>=0&&dy<m&&map[dx][dy]!='#')
{
next.x=dx;
next.y=dy;
next.step=cur.step+1;
if(map[dx][dy]=='r')///found
return next.step;
else if(map[dx][dy]=='.')
q.push(next);
else
{
next.step++;
q.push(next);
}
map[dx][dy]='#';///mark
}
}
}
return -1;
}
int main()
{
int i,j,ans,sx,sy;
while(cin>>n>>m)
{
for(i=0;i<n;i++)
{
scanf("%s",map[i]);
for(j=0;j<m;j++)
if(map[i][j]=='a')///locate
{
sx=i;
sy=j;
}
}
ans=BFS(sx,sy);
if(ans==-1)
printf("Poor ANGEL has to stay in the prison all his life.\n");
else
printf("%d\n",ans);
}
return 0;
}

​

## 挑战ACM迷宫 HNUSTOJ

10
1 0 2 1 2 3 4 2 2 5
2 1 0 1 3 2 5 7 2 1
1 1 1 0 1 1 1 3 2 3
1 2 1 1 0 1 1 1 2 3
1 1 2 0 2 0 2 3 2 3
2 2 2 2 3 2 0 2 3 2
3 2 2 2 1 1 1 0 1 1
0 1 1 3 0 1 1 2 3 2
2 0 1 1 2 2 2 2 2 2
3 2 3 2 3 2 3 2 3 2
5
1 2 1 2 5
1 3 2 4 5
2 1 0 2 5
2 1 1 2 1
2 4 1 1 2

min=29
min=11

​
​
///逃离ACM迷宫 HNUSTOJ
#include <cstdio>
#include <cstring>
#include <string>
#include <iostream>
#include <queue>
#include <algorithm>
using namespace std;
///用优先队列存结果
int map[109][109];
int vis[109][109];
int dir[4][2]={{1,0},{0,1},{-1,0},{0,-1}};
int n;
typedef struct node
{
int x,y,val;///坐标、走到到这里所用的时间
bool operator <(const node x)const
{
return val > x.val;
}
}node;

int BFS(int n)
{
priority_queue<node> q;
node t;
int sx,sy,ex,ey,nx,ny;
sx=sy=1,ex=ey=n;
t.x=sx, t.y=sy, t.val=map[sx][sy];
vis[sx][sy]=1;
q.push(t);
while(!q.empty())
{
t=q.top();
q.pop();
if(t.x==ex && t.y==ey)///因为是优先队列存的，所以弹出来第一个满足条件的即为所求
return t.val;
for(int i=0;i<4;i++)
{
nx=t.x+dir[i][0];
ny=t.y+dir[i][1];
if(nx>0&&nx<=n && ny>0&&ny<=n && map[nx][ny]
&& !vis[nx][ny])
{
vis[nx][ny]=1;
q.push((node){nx,ny,t.val+map[nx][ny]});
}
}
}
return -1;
}
int main()
{
while(cin>>n)
{
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
scanf("%d",&map[i][j]);
memset(vis,0,sizeof(vis));
printf("min=%d\n",BFS(n));
}
return 0;
}

​

​

## 中国象棋中的跳马问题 HNUSTOJ

2
9 10
1 1 2 3
0
9 10
1 1 2 3
8
1 2
2 2
3 3
3 4
1 4
3 2
2 4
1 3

1
can not reach!

此题是一个搜索题，可用DFS或BFS，建议选择BFS(广搜)。一开始把马的起始点加入队列，然后用广搜的思想把此点能到达的其他点加入队列，这里需要一个数组用来记录此点在之前是否已经加入队列，如果加入过队列当中，就不需要再加入了，直到队列里的元素为空，或者搜索到了终点，搜索即停止，然后输出相应答案即可。

-------------------------------------------------------------------------------------------------------------------------------------------------------------------------

1）路障所占的地方不可以作为出发点和到达点，1个位置。

2）路障可以ban马的行走路线，8个位置

（大家可以自己画个图，理解下。）

position dir[8]={{-2,-1},{-2,1},{-1,2},{1,2},{2,1},{2,-1},{1,-2},{-1,-2}};

bar  cir[8]={{-1,0},{-1,0},{0,1},{0,1},{1,0},{1,0},{0,-1},{0,-1}};

​
///中国象棋中的跳马问题 HNUSTOJ
#include <cstdio>
#include <cstring>
#include <string>
#include <iostream>
#include <queue>
#include <algorithm>
using namespace std;

struct position
{
int x;
int y;
};
struct node
{
int x;
int y;
int sum;
};
int vis[109][109];
int barrier[109][109];
position dir[8]={{-2,-1},{-2,1},{-1,2},{1,2},{2,1},{2,-1},{1,-2},{-1,-2}};
position cir[8]={{-1,0},{-1,0},{0,1},{0,1},{1,0},{1,0},{0,-1},{0,-1}};
queue<node> que;
int ans,n,m,sx,sy,ex,ey,flag;

int is_right(int a,int b)
{
if(a>0&&a<=n&&b>0&&b<=m)
return 1;
return 0;
}
int BFS()
{
int row,col,tx,ty;
ans=flag=0;
node t,m;
t.x=sx,t.y=sy,t.sum=ans;
que.push(t);
vis[sx][sy]=1;
while(!que.empty())
{
t=que.front();
que.pop();
row=t.x,col=t.y;
if(row==ex && col==ey)
{
flag=1;
ans=t.sum;
return ans;
}
for(int i=0;i<8;i++)
{
tx=t.x+dir[i].x;
ty=t.y+dir[i].y;
if(!barrier[t.x+cir[i].x][t.y+cir[i].y])
{///没有障碍
if(is_right(tx,ty) &&!vis[tx][ty] && !barrier[tx][ty])
{
m.x=tx;
m.y=ty;
m.sum=t.sum+1;
que.push(m);
vis[tx][ty]=1;
}
}
}
}
}
int main(void)
{
int t,ob,obx,oby;
cin>>t;
while(t--)
{
while(!que.empty())
que.pop();
memset(vis,0,sizeof(vis));
memset(barrier,0,sizeof(barrier));
scanf("%d%d%d%d%d%d%d",&n,&m,&sx,&sy,&ex,&ey,&ob);
for(int i=0;i<ob;i++)
{
scanf("%d%d",&obx,&oby);
vis[obx][oby]=1;
barrier[obx][oby]=1;
}
BFS();
if(flag==1)
printf("%d",ans);
else
printf("can not reach!");
if(t)
printf("\n");
}
return 0;
}

​

## 八数码问题 HNUSTOJ

 1 2 3 1 2 3 8 0 4 7 8 4 7 6 5 0 6 5

初始状态                                                                                            目标状态

8 0 4
7 6 5
1 2 3
7 8 4
0 6 5

2

▷▶用BFS分散，当前状态的下一状态的多种情况即“多个方向”，需要设置判重函数，如果重复了状态那么就不往这个状态发散（即不再走这条路），这里可以用设置一个全排列的数组判重，效率高但是不太好写，也可以用STL里的set的特性判重，代码容易写但是效率不高（何以双全呐，都不容易嘛，好好提高自己吧 （=v=））

关于判重的方法

▶也可以用生成哈希表数组来判重

▶另外，还可以用康托展开运算得到一个数组，原理也是数组判重，非常奇妙的一个思想，有兴趣的看一下

公式▷▶▷▶             X=an(n-1)!+an-1(n-2)!+...+a1*0!

康托展开的逆运算
既然康托展开是一个双射，那么一定可以通过康托展开值求出原排列，即可以求出n的全排  列中第x大排列。
如n=5,x=96时：
首先用96-1得到95，说明x之前有95个排列.(将此数本身减去1)用95去除4! 得到3余23，说明有3  个数比第1位小，所以第一位是4.用23去除3! 得到3余5，说明有3个数比第2位小，所以是4，但  是4已出现过，因此是5.用5去除2!得到2余1，类似地，这一位是3.用1去除1!得到1余0，这一位  是2.最后一位只能是1.所以这个数是45321。

-------------可以求出某个组合在其全排列里面的顺序X，也可以根据X推出是哪个排列。
-----------------------------------------------------------------------------------

static const int FAC[] = {1, 1, 2, 6, 24, 120, 720, 5040, 40320, 362880};   // 阶乘

void decantor(int x, int n)
{
vector<int> v;  // 存放当前可选数
vector<int> a;  // 所求排列组合
for(int i=1;i<=n;i++)
v.push_back(i);
for(int i=m;i>=1;i--)
{
int r = x % FAC[i-1];
int t = x / FAC[i-1];
x = r;
sort(v.begin(),v.end());// 从小到大排序
a.push_back(v[t]);      // 剩余数里第t+1个数为当前位
v.erase(v.begin()+t);   // 移除选做当前位的数
}
}

///八数码问题 set判重
#include <iostream>
#include <set>
#include <cstring>
#include <queue>
#include <cstdio>
#define MAXSIZE 1000000
using namespace std;

typedef int state[9];  ///定义状态数组
state st[MAXSIZE],goal;
int dist[MAXSIZE];
set<int> vis;
int dir[4][2]={{-1,0},{0,-1},{1,0},{0,1}};

int try_to_insert(int s)
{
int v=0;
for(int i=0;i<9;i++)
v = v * 9 + st[s][i];
if(vis.count(v)) ///重复了，跳出
return 0;
vis.insert(v);
return 1;
}

int BFS()
{
vis.clear();
int front = 1,rear = 2;
while(front < rear)
{
state& s = st[front];
if(memcmp(goal, s, sizeof(s))==0)///找到，返回
return front;
int z;
for(z=0; z<9; z++)///找'0'的位置
if(!s[z]) break;
int x = z/3, y = z%3;
for(int i=0;i<4;i++)///四方发散找
{
int tx = x + dir[i][0];
int ty = y + dir[i][1];
int tz = tx * 3 + ty;
if(tx>=0 && tx<3 && ty>=0 && ty<3)
{
state& t = st[rear];
memcpy(&t, &s, sizeof(s));///扩展新状态，将s状态的9个值都复制到t
t[tz] = s[z];///因为空白格移动，修改t中相关的两个值
t[z] = s[tz];
dist[rear] = dist[front] + 1;///更新 新状态的距离值
if(try_to_insert(rear))///不重复，入队
rear++;
}
}
front++;///扩展完毕，修改队首指针，为下一次出队列准备
}
return 0;
}

int main()
{
for(int i=0;i<9;i++)
scanf("%d",&st[1][i]);
for(int i=0;i<9;i++)
scanf("%d",&goal[i]);
int ans=BFS();
if(ans > 0)
printf("%d\n",dist[ans]);
else
printf("Impossible\n");
return 0;
}

=======遇到好的题目会回来继续更新======

===如果各位大牛牪犇路过发现问题欢迎反映  (｢･ω･)｢嘿===

11-30
03-20 3218

05-07 6万+
10-29 3607
11-16 13万+
03-02 1万+
08-08 3639
11-04 2万+
06-28
12-26 1164