基础算法(3)、搜索与模拟

搜索(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;
}


  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值