广度优先算法思路
在一个游戏中,玩家处于一个如下所示12行12列的迷宫:
0,1,0,0,0,1,1,1,0,1,0,1
0,0,0,1,0,0,0,0,1,0,0,1
0,1,0,1,0,1,1,1,0,1,0,0
0,1,0,0,0,0,0,1,0,0,1,1
0,0,0,0,1,0,0,0,0,0,0,0
0,0,1,0,0,0,1,0,0,0,1,0
0,0,1,0,0,0,0,0,1,0,0,0
1,0,0,1,0,1,0,0,0,1,0,1
0,0,1,0,1,0,1,0,1,0,0,0
0,0,0,0,0,1,0,0,0,1,1,0
0,0,0,0,0,1,0,0,0,0,0,0
0,1,0,1,0,0,0,1,0,1,0,0
其中迷宫由0,1组成,0表示道路,1表示障碍物。
现在要根据玩家和游戏中被攻击的虚拟boss所在位置,给玩家以最近距离的提示。
最近距离:
即玩家走到boss所走的最少步数。(注:路线中的一步是指从一个坐标点走到其上下左右相邻坐标点。)
输入格式:
输入4个整数a,b,c,d(即玩家和虚拟boss在迷宫中的坐标位置分别为(a,b) 、(c,d)), 其中
0<=a,b,c,d<12。
输出格式:
输出在迷宫中从(a,b)出发到达(c,d)的最少步数,如果(a,b)永远无法到达(c,d)则输出10000。
输入样例:
在这里给出一组输入。
例如: 0 0 11 11 结尾无空行
输出样例:
在这里给出相应的输出。
例如:
22
回溯特别容易超时(尤其迷宫扩大后),可以按一般广搜(即用先进先出队列的广搜)或A*算法(优先队列式分枝限界法,简称优先队列式广搜)去改写。
回溯法:
#include<iostream>
using namespace std;
int mg[9][9]=
{
{1,1,1,1,1,1,1,1,1},
{1,0,0,1,0,0,1,0,1},
{1,0,0,1,1,0,0,0,1},
{1,0,1,0,1,1,0,1,1},
{1,0,0,0,0,1,0,0,1},
{1,1,0,1,0,1,0,0,1},
{1,1,0,1,0,1,0,0,1},
{1,1,0,1,0,0,0,0,1},
{1,1,1,1,1,1,1,1,1}
};
int n,a,b,c,d,Min,step;
int offset[4][2]={{1,0},{-1,0},{0,1},{0,-1}};
int jianzhi(int x,int y,int i)
{
int x1=x+offset[i][0],y1= y+offset[i][1];
if(x1<0 || x1>8 || y1<0 || y1>8) return 0;
if(mg[x1][y1]) return 0;
if(step>Min) return 0;
return 1;
}
void f(int x,int y)
{//回溯最大的缺点,运算量难以控制
if(x==c && y==d)
{
if(step-1<Min) Min=step-1;
}
else for(int i=0;i<4;i++)
if(jianzhi(x,y,i))
{ int x1=x+offset[i][0],y1= y+offset[i][1];
mg[x1][y1] = step;
step++;
f(x1,y1);
mg[x1][y1] = 0;
step--;
}
}
int main()
{
cin>>n;
while(n--)
{
cin>>a>>b>>c>>d;
Min=100;
step=1;
f(a,b);
cout<<Min<<endl;
}
return 0;
}
例如:
左上角(0,0)
起点(0,3)-> 终点 (2,2)
一般队列式分支限界算法搜索过程中生成的解空间树(前4层结点)为:【结点内数据:坐标(i,j), 根到当前结点已走步数step(代价)】
优先级队列式分支限界算法搜索过程中生成的解空间树(前4层结点)为:【结点内数据:坐标(i,j), 根到当前结点已走步数step(代价),当前结点到目标评估代价len】
广搜(即用先进先出队列的广搜)
#include<iostream>
#include<memory.h>
#include<queue>
using namespace std;
int mg[9][9]=
{
{1,1,1,1,1,1,1,1,1},
{1,0,0,1,0,0,1,0,1},
{1,0,0,1,1,0,0,0,1},
{1,0,1,0,1,1,0,1,1},
{1,0,0,0,0,1,0,0,1},
{1,1,0,1,0,1,0,0,1},
{1,1,0,1,0,1,0,0,1},
{1,1,0,1,0,0,0,0,1},
{1,1,1,1,1,1,1,1,1}
};
int n,a,b,c,d,m[9][9];
int offset[4][2]={{1,0},{-1,0},{0,1},{0,-1}};
struct node{ int x,y,step;};//起点到(x,y),已经走了step步骤
int jianzhi(node e,int i)
{ int x1,y1;
x1 = e.x+offset[i][0];
y1 = e.y+offset[i][1];
if(x1<0 || x1>8 || y1<0 || y1>8) return 0;
if(m[x1][y1] || mg[x1][y1]) return 0;//(x1,y1)为已跳过、障碍物,m[][],mg[][]
return 1;
}
int main()
{
int i;
node e,e1;
cin>>n;
queue<node>Q;
while(n--)
{
cin>>a>>b>>c>>d;
e.x = a; e.y = b; e.step = 0; //根节点
m[a][b]=1;
Q.push(e);
memset(m,0,sizeof(m));
while(!Q.empty())
{
e=Q.front();
Q.pop();
if(e.x==c && e.y==d) break;
for(i=0;i<4;i++)
if(jianzhi(e,i))
{
e1.x = e.x + offset[i][0];
e1.y = e.y + offset[i][1];
e1.step = e.step +1;
m[e1.x][e1.y]=1;
Q.push(e1);
}
}
if(e.x ==c && e.y ==d) cout<<e.step<<endl ;
else cout<<"无法到达"<<endl;
while(!Q.empty()) Q.pop();
}
return 0;
}
A*算法(优先队列式分枝限界法,简称优先队列式广搜)
#include<iostream>
#include<memory.h>
#include<queue>
#include<algorithm>
using namespace std;
int mg[9][9]=
{
{1,1,1,1,1,1,1,1,1},
{1,0,0,1,0,0,1,0,1},
{1,0,0,1,1,0,0,0,1},
{1,0,1,0,1,1,0,1,1},
{1,0,0,0,0,1,0,0,1},
{1,1,0,1,0,1,0,0,1},
{1,1,0,1,0,1,0,0,1},
{1,1,0,1,0,0,0,0,1},
{1,1,1,1,1,1,1,1,1}
};
int n,a,b,c,d,m[9][9];
int offset[4][2]={{1,0},{-1,0},{0,1},{0,-1}};
struct node{
int x,y,step,len;
bool operator < (const node &a) const
{
return step+len > a.step+a.len; //小的优先排队头
}
};
//起点(a,b)到(x,y),已经走了step步骤,到(c,d)至少还要走len步
int jianzhi(node e,int i)
{ int x1,y1;
x1 = e.x+offset[i][0];
y1 = e.y+offset[i][1];
if(x1<0 || x1>8 || y1<0 || y1>8) return 0;
if(m[x1][y1] || mg[x1][y1]) return 0;//(x1,y1)为已跳过、障碍物,m[][],mg[][]
return 1;
}
int main()
{
int i;
node e,e1;
cin>>n;
priority_queue<node>Q;
while(n--)
{
cin>>a>>b>>c>>d;
e.x = a; e.y = b; e.step = 0; e.len = abs(a-c)+abs(b-d);//根节点
m[a][b]=1;//(a,b)点已经进入队列
Q.push(e);
memset(m,0,sizeof(m));
while(!Q.empty())
{
e=Q.top();
Q.pop();
if(e.x==c && e.y==d) break;
for(i=0;i<4;i++)
if(jianzhi(e,i))
{
e1.x = e.x + offset[i][0];
e1.y = e.y + offset[i][1];
e1.step = e.step +1;
e1.len = abs(e1.x-c)+abs(e1.y-d);//评估代价
m[e1.x][e1.y]=1;//已经进入队列标志
Q.push(e1);
}
}
if(e.x ==c && e.y ==d) cout<<e.step<<endl ;
else cout<<"无法到达"<<endl;
while(!Q.empty()) Q.pop();
}
return 0;
}
输出路径
#include<iostream>
#include<memory.h>
#include<queue>
#include<algorithm>
#include<cstdio>
using namespace std;
int mg[20][20]=
{
{0,1,0,0,0,1,1,1,0,1,0,1},
{0,0,0,1,0,0,0,0,1,0,0,1},
{0,1,0,1,0,1,1,1,0,1,0,0},
{0,1,0,0,0,0,0,1,0,0,1,1},
{0,0,0,0,1,0,0,0,0,0,0,0},
{0,0,1,0,0,0,1,0,0,0,1,0},
{0,0,1,0,0,0,0,0,1,0,0,0},
{1,0,0,1,0,1,0,0,0,1,0,1},
{0,0,1,0,1,0,1,0,1,0,0,0},
{0,0,0,0,0,1,0,0,0,1,1,0},
{0,0,0,0,0,1,0,0,0,0,0,0},
{0,1,0,1,0,0,0,1,0,1,0,0}
};//迷宫12行12列
int n=12,a,b,c,d,m[20][20];//n是迷宫行列数,m[][]辅助判断是否已经进入队列
int offset[4][2]={{1,0},{-1,0},{0,1},{0,-1}};
struct node{
int x,y,step,len;
struct node *parent;
};//起点(a,b)到当前点(x,y),已经走了step步,到(c,d)至少还要走len步
struct cmp
{
bool operator () (const node *a,const node *b)
{
return a->step+a->len > b->step+b->len; //小的优先排队头
}
};
void outputpath(node *e)
{
int path[20][20]={0},i,j;
for(i=0;i<n;i++)//拷贝迷宫数组到path[][]
for(j=0;j<n;j++) path[i][j] = mg[i][j];
//从e到根,将对应步数写入path[][]
while(e)
{
path[e->x][e->y] = e->step+1; //根结点step值为0,所以全部按+1处理
e = e->parent ;
}
for(i=0;i<n;i++)//输出path[][]
{
for(j=0;j<n;j++)
printf("%3d",path[i][j]);
printf("\n");
}
}
node* jianzhi(node *e,int i)
{
node *e1;
int x1,y1;
x1 = e->x+offset[i][0];
y1 = e->y+offset[i][1];
if(x1<0 || x1>=n || y1<0 || y1>=n) return 0;
if(m[x1][y1] || mg[x1][y1]) return 0;//(x1,y1)为已跳过、障碍物,m[][],mg[][]
e1 = new node;
e1->x = e->x + offset[i][0];
e1->y = e->y + offset[i][1];
e1->step = e->step +1;
e1->len = abs(e1->x-c)+abs(e1->y-d);//评估代价
e1->parent = e;
return e1;
}
int main()
{
int i;
node *e,*e1;
priority_queue<node*,vector<node*>,cmp>Q;
cin>>a>>b>>c>>d;
e = new node; //创建根节点
e->x = a; e->y = b;
e->step = 0;
e->len = abs(a-c)+abs(b-d);//根节点到目标的评估距离
e->parent = NULL;
Q.push(e);
memset(m,0,sizeof(m));
m[a][b]=1;//(a,b)点已经进入队列
while(!Q.empty())
{
e=Q.top();
Q.pop();
if(e->x==c && e->y==d) break;
for(i=0;i<4;i++)
if(e1=jianzhi(e,i))
{
m[e1->x][e1->y]=1;//已经进入队列标志
Q.push(e1);
}
}
if(e->x ==c && e->y ==d)
{
cout<<e->step<<endl;
outputpath(e);//沿着e结点往上访问祖先结点,从而确定路线并输出。
}
else cout<<"无法到达"<<endl;
while(!Q.empty()) Q.pop();
return 0;
}