搜索(search)也是计算机常见的一种操作。常见的搜索有深度优先搜索(DFS)和广度优先搜索(BFS)。
而不管是深搜还是广搜,实际上都隶属于图论中的算法。深搜的基本思路如下:
1、访问结点
2、对与其关联的点进行深搜直至所有的结点均被访问过
3、若图中尚有结点未被访问,则从此开始继续深搜
深搜被发明的时候,人们常用它来寻找迷宫的解,事实上,广搜也可以达到相同的目的。广搜的思路如下:
1、访问结点并将其入队列
2、队头出队,并寻找与其相关联的结点,将其入队列
3、若队空则结束
从这两个算法的思路不难看出,深搜与广搜的最大区别就是深搜一条道走到黑,而广搜则一层层寻找答案。
所以我们能发现,深搜用栈(stack)实现,广搜用队列(queue)实现。
关于图(graph)的更多算法,将会在稍后推出。
关于模拟,其实并没有太多好说的。就是按照题目要求用程序把某个过程展现出来。
需要注意的是,一般模拟题难度都不会很大,但代码量会比较繁复,极其考验基本功,需要大量注意细节。
今天主要讲的是搜索。搜索的技巧有很多,包括剪枝,堆优化,哈希表优化等等。
本日例题为
Saving Tang Monk
HDU - 5025题意: 孙悟空需要营救唐僧。给出n*n的网格,有且仅有一个K和一个T,分别代表孙悟空和唐僧的位置。
最多有m把钥匙,最多5条蛇。走的方向只能是上下左右,每走一步需要时间为1。走到有蛇(S)的格子
需要杀蛇,总时间消耗2(杀蛇1走路1),并且蛇杀完即消失。图中有特殊障碍格不能走到。
问能否从K走到T并且按顺序取得所有钥匙,如果能则输出最短的时间。
分析:本题可以算是比较基础的BFS+状压题了,或者可以看做求解最短路径。这要看我们是否使用优先队列。
如果不使用,则是深搜;使用的话,可以看做Dijkstra+heap维护。标程中使用的是优先队列。
我们这儿开一个三维数组,前两维存坐标,最后一维存钥匙状态即当前获得了几把钥匙。
由于只有五条蛇,我们使用5位二进制数表示蛇的状态,将会比较方便。直接设成四维数组也是可以的。
蛇只需要杀一次,所以我们用优先队列维护每一次搜索花费的最小步数。
剩下的就是注意好判断条件了。代码如下:
#include<bits/stdc++.h>
using namespace std;
const int maxn=115;
int n,m;
int dx[4]={-1,1,0,0};
int dy[4]={0,0,-1,1};
char mp[maxn][maxn];
struct Node{
int x;
int y;
int key,time,snake;
friend bool operator<(Node a,Node b){
return b.time<a.time;
}
};//结构体存储信息
priority_queue<Node>q;
bool vis[maxn][maxn][11];
int snake[maxn][maxn];
Node st,en;
bool check(int x,int y){
if(x<=n&&x>=1&&y<=n&&y>=1&&mp[x][y]!='#')
return true;
return false;
}//检查边界条件
int bfs(){
int xx,yy;
while(!q.empty()) {q.pop();}//初始化优先队列
Node cur,next;
memset(vis,false,sizeof(vis));
vis[st.x][st.y][0]=true;
q.push(st);
while(!q.empty()){
cur=q.top();
q.pop();
if(cur.x==en.x&&cur.y==en.y&&cur.key==m)//到达终点,则搜索结束
return cur.time;
for(int i=0;i<4;i++){
xx=cur.x+dx[i];
yy=cur.y+dy[i];
if(check(xx,yy)&&!vis[xx][yy][cur.key]){//当前位置合法
if(mp[xx][yy]==cur.key+1+'0'){//当前位置为钥匙,并且正好是下一把需要拿的钥匙
vis[xx][yy][cur.key+1]=true;
next.x=xx;
next.y=yy;
next.snake=cur.snake;
next.key=cur.key+1;
next.time=cur.time+1;
q.push(next);
}
else if(mp[xx][yy]=='S'){//当前位置是蛇
if(!(cur.snake&(1<<snake[xx][yy]))){//蛇还没被杀
next.x=xx;
next.y=yy;
next.snake=cur.snake|(1<<snake[xx][yy]);
next.time=cur.time+2;
next.key=cur.key;
vis[xx][yy][next.key]=true;
q.push(next);
}
else{//蛇已经被杀
next.x=xx;
next.y=yy;
next.snake=cur.snake;
next.time=cur.time+1;
next.key=cur.key;
vis[xx][yy][next.key]=true;
q.push(next);
}
}
else{//其他情况
next.x=xx;
next.y=yy;
next.snake=cur.snake;
next.time=cur.time+1;
next.key=cur.key;
vis[xx][yy][next.key]=true;
q.push(next);
}
}
}
}
return -1;
}
void solve(){
char str[maxn];
memset(snake,-1,sizeof(snake));
int cnt=0;
for(int i=1;i<=n;i++){
scanf("%s",str+1);
for(int j=1;j<=n;j++){
mp[i][j]=str[j];
if(mp[i][j]=='K'){//初始化st为K所在位置信息
st.x=i;
st.y=j;
st.key=0;
st.snake=0;
st.time=0;
}
else if(mp[i][j]=='T'){//初始化en为T所在位置信息
en.x=i;
en.y=j;
en.key=m;
}
else if(mp[i][j]=='S'){//初始化蛇的位置信息
snake[i][j]=cnt++;
}
}
}
}
int main(){
while(~scanf("%d%d",&n,&m)){
if(n==0&&m==0) return 0;
solve();
int ans=bfs();
if(ans==-1) printf("impossible\n");
else printf("%d\n",ans);
}
return 0;
}