迷宫问题:
定义一个二维数组:
int maze[5][5] = {
0, 1, 0, 0, 0,
0, 1, 0, 1, 0,
0, 0, 0, 0, 0,
0, 1, 1, 1, 0,
0, 0, 0, 1, 0,
};
它表示一个迷宫,其中的1表示墙壁,0表示可以走的路,只能横着走或竖着走,不能斜着走,要求编程序找出从左上角到
右下角的最短路线。
Input 一个5 × 5的二维数组,表示一个迷宫。数据保证有唯一解。
Output 左上角到右下角的最短路径,格式如样例所示。
Sample Input
0 1 0 0 0
0 1 0 1 0
0 0 0 0 0
0 1 1 1 0
0 0 0 1 0
Sample Output
(0, 0) (1, 0) (2, 0) (2, 1) (2, 2) (2, 3) (2, 4) (3, 4) (4, 4)
广度优先搜索(BFS)
简要思想:
1、从初始状态s(起点)开始,利用规则(直线走:4个方向,加入斜线八个方向),生成下一层的状态(相邻点)。
2、顺序检查下一层的所有状态,看是否出现目标状态des,否则就对该层所有状态点,分别利用规则,再生成再下一层的所有状态点。
3、继续按照上面的思想生成再下一层的所有状态节点,这样层层往下展开,直到出现目标状态节点为止。
其中特别注意的是若要记录经过点的路径,要创建一个数组pre[N*N],节点ID对应的数组值pre[ID]==节点ID的前一个节点id,最后用递归的方法打印出路径节点。
程序用队列实现(queue)
#include <iostream>
#include <queue>
using namespace std;
void print(int des,int pre[30]) //递归实现逆推路线的打印
{
if(pre[des]!=-1)
print(pre[des],pre);
cout<<'('<<des/5<<','<<des%5<<')'<<endl;
}
void BFS(int maze[5][5])
{
int pre[30];//存储前一个节点
queue<int> que;//定义que队列存储节点ID
int vis[30];//是否被访问
memset(vis,0,sizeof(vis));
pre[0]=-1;
que.push(0);//将起始点存入队列,并涂黑
vis[0]=1;
int dir[4][2]={{1,0},{-1,0},{0,1},{0,-1}};
int now,row,col,nr,nc,next;
while(!que.empty())
{
now=que.front();
que.pop();//取出队列头的节点
row=now/5;//得到节点的坐标位置
col=now%5;
for(int i=0;i<4;i++)//分别检测所取的节点的上下左右节点
{
nr=row+dir[i][0];
nc=col+dir[i][1];
next=nr*5+nc;//坐标换算成节点ID
if(nr>=0&&nr<=5&&nc>=0&&nc<=5&&maze[nr][nc]==0&&!vis[next])
{
pre[next]=now;//记录next节点的上一节点
//if(next==24) return;//到达终点
vis[next]=1;
que.push(next);
}
}
}
print(24,pre);//24为(4,4)的节点ID
}
void main()
{
int maze[5][5];
for(int i=0;i<5;i++)
for(int j=0;j<5;j++)
cin>>maze[i][j];
BFS(maze);
}
核心表述:
void BFS()
{
初始化队列Q;
Q={起始点s};
标记s为已访问;
while(Q非空)
{
取出队列Q的首元素n;
n出列;
//if(n==目标点){……}
所有与n相邻且未被访问的点进入队列;
标记n为已访问
}
//完成遍历所有的点
}
广度优先算法详解:http://blog.csdn.net/raphealguo/article/details/7523411
以上实例步骤:
1.起始节点Vs变成灰色,加入队列Q,Q={(0,0)}
2.取出队列Q的头一个节点Vn,Vn={0,0},Q={}
3.把Vn={0,0}染成黑色,取出Vn所有相邻的白色节点{(1,0)}
4.不包含终点(4,4),染成灰色,加入队列Q,Q={(1,0)}
5.取出队列Q的头一个节点Vn,Vn={1,0},Q={}
6.把Vn={1,0}染成黑色,取出Vn所有相邻的白色节点{(2,0)}
7.不包含终点(4,4),染成灰色,加入队列Q,Q={(2,0)}
8.取出队列Q的头一个节点Vn,Vn={2,0},Q={}
9.把Vn={2,0}染成黑色,取出Vn所有相邻的白色节点{(2,1), (3,0)}
10.不包含终点(4,4),染成灰色,加入队列Q,Q={(2,1), (3,0)}
11.取出队列Q的头一个节点Vn,Vn={2,1},Q={(3,0)}
12. 把Vn={2,1}染成黑色,取出Vn所有相邻的白色节点{(2,2)}
13.不包含终点(4,4),染成灰色,加入队列Q,Q={(3,0), (2,2)}
14.持续下去,知道Vn的所有相邻的白色节点中包含了(4,4)……
15.此时获得了答案
深度优先搜索(DFS)
基本思想:
1、从初始状态S开始,利用规则生成搜索树下一层任一个状态节点;
2、看是否是目标状态节点des,若否,依此状态节点利用规则生成再下一层任一个节点,继续检查是否为目标状态节点,若否继续以上操作,一直进行到叶节点(即不能再生成新的状态节点,最底部)。
3、若叶节点仍不是目标状态节点,回溯到上一层结果,取另一可能扩展搜索的分支,依次生成新的状态节点,继续往下扩展,直到该支叶节点,若此过程仍未遇到目标节点,那么再次采用相同的回溯方法推导上层节点,扩展可能的分支生成新状态,一直继续下去直到找到目标状态点为止。 实例表述:
http://wenku.baidu.com/view/5e81f56152ea551811a6870a?fr=prin
深度和广度相比较
一般用广度来求解最优值(最短路径),但当N*N矩阵N很大时随着每一层的元素加入,则队列需要很大的存储空间,内存消耗很大;但对于深度来说,每次搜的过程,每一层只需要维护一个节点,但DFS只能寻找有解,而无法寻找最优解。
上题需改为:
定义一个二维数组:
int maze[5][5] = { 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 1, 0, };
它表示一个迷宫,其中的1表示墙壁,0表示可以走的路,只能横着走或竖着走,不能斜着走,要求编程序找出从左上角到右下角的一条路线。
用堆栈来实现深度搜索过程
(类比)
#include <iostream>
using namespace std;
int dir[4][2]={{0,1},{0,-1},{1,0},{-1,0}};
int maze[10][10]={
0,1,0,0,0,0,0,0,0,0,
0,1,1,0,1,1,1,0,0,0,
0,0,0,0,0,0,0,0,0,0,
1,1,1,0,1,0,0,0,0,0,
0,1,0,0,1,0,1,1,1,0,
0,1,0,0,1,1,1,1,1,0,
0,0,0,1,1,0,0,0,1,0,
0,1,0,0,0,0,1,0,1,0,
0,1,1,1,0,1,1,0,1,1,
0,0,0,0,0,0,1,0,0,0,
};
int table[10][10]={0};
int step=-1;
int stepcomplete=0;
int allcomplete=0;
int Act[1000]={0};//每一步节点的四个方向值为1-4
int x=0,y=0;
void test()//测试是否已达到目标
{
if(x==9&&y==9)
{
stepcomplete=allcomplete=1;
cout << "Get to destination Success" << endl;
}
}
int actOK()
{
int nextX=x+dir[Act[step]-1][0];
int nextY=y+dir[Act[step]-1][1];
if (Act[step] >4)//方向是否错误
return 0;
if(nextX >= 10 || nextX < 0)//x坐标是否越界
return 0;
if(nextY >= 10|| nextY < 0)//y坐标是否越界
return 0;
if(table[nextX][nextY] == 1)//是否已达到过
return 0;
if(maze[nextX][nextY] == 1)//是否有障碍
return 0;
x=nextX;
y=nextY;
table[x][y]=1;
return 1;
}
void back()//到达死胡同时返回上一节点
{
table[x][y]=0;
x-=dir[Act[step-1]-1][0];
y-=dir[Act[step-1]-1][1];
Act[step]=0;//方向重新归位
step--;//返回上一步
}
void DFS()
{
table[x][y]=1;//起点标记已访问
while(!allcomplete)
{
step++;
stepcomplete=0;
while(!stepcomplete)//对该点的四个相邻点进行判断,如果检测到有效点直接执行外循环,
{ //若无改变方向,继续检测,直到四个都检测完,若检测为死胡同则返回上一节点,从没有检测的方向继续检测,依次进行
Act[step]++;//改变方向
if(actOK())//检测到有效点,点替换,重新外循环,重新检测新点的邻近有效点
{
test();//检测新点是否为终点
stepcomplete=1;//进入下一个外循环
}
else
{
if(Act[step]>4)
back();//已经搜寻完4个方向无有效点,回退到上个节点,重新内循环
if(step<0)//全部路径搜寻完,没有找到目标点,退出
stepcomplete=allcomplete=1;
}
}
}
}
void print()
{
cout<<"The path is:"<<endl;
for(int i=0;i<10;i++)
{
for(int j=0;j<10;j++)
cout<<table[i][j]<<' ';
cout<<endl;
}
for(int i=0;i<10;i++)
{
for(int j=0;j<10;j++)
{
if(table[i][j]==1)
cout<<'('<<i<<','<<j<<')'<<"->";
}
}
}
void main()
{
DFS();
print();
}
注:其主要思想是,对一个点的四个邻接点依次进行检测当检测到有效点(点的坐标在图内且未被访问过),直接对这个有效点进行新一轮的邻接有效点检测,直到遇到终点;若遇到死胡同,则返回上一点,继续从未检测的邻接点开始寻找有效点(又开辟了一条新路径),若无有效点继续返回上一点,依次进行。