百炼4116 拯救行动&&4130 Saving Tang Monk&&4115 鸣人与佐助 简单BFS搜索题型总结对比

一、

百炼4116拯救行动(OpenJudge - 4116:拯救行动

        这题就是在简单BFS的基础上加了一个守卫,击杀一次守卫时间也需要+1,其他照常按照普通BFS的思路就行,最主要就是可以把击杀守卫看成是经过两次这一个点,这样就不用对击杀守卫做时间+2的特殊处理。这题与下面要讲的两题不同的地方就是被另一条(因为BFS是很多条路同时搜索)BFS所走路径击杀的守卫在其他路径上就不必在次击杀了(如果该守卫已经被击杀了那么一定有另一条路比当前路径更短,所以此路就没有必要再往这边走了)如果明白了这一点画个图就很任意区分这几题的区别。

先解释一下怎么样把击杀守卫看成两次经过这个点,先看图

struct point{
    int x;
    int y;
    int step;
    int flag;
    point(int a,int b,int c,int d):x(a),y(b),step(c),flag(d){}
    point(){}
};

首先用flag来标记当前位置是否是守卫。

if(u.flag==1)
{
     r.push(point(u.x,u.y,u.step+1,0));
     continue;
}

当前位置的flag==1的时候会再次把当前点入队(如上图),就相当于走了两次这个位置。

下面是本题ac代码

#include<stdio.h>
#include<iostream>
#include<queue>
using namespace std;
char mp[210][210];
int v[210][210];
int xr,yr,xa,ya;
    int n,m;
int Dx[4]={0,1,0,-1};
int Dy[4]={1,0,-1,0};
struct point{
    int x;
    int y;
    int step;
    int flag;
    point(int a,int b,int c,int d):x(a),y(b),step(c),flag(d){}
    point(){}
};
struct point u,p;
int Bfs()
{
    queue<point> r;
    r.push(point(xr,yr,0,0));
    while(!r.empty())
    {
        u=r.front();
        r.pop();
        if(u.flag==1)
        {
            r.push(point(u.x,u.y,u.step+1,0));
            continue;
        }
        if(mp[u.x][u.y]=='a')
        {
            return u.step;
        }

        for(int i=0;i<4;i++)
        {
            p.x=u.x+Dx[i];
            p.y=u.y+Dy[i];

            if(p.x>=0&&p.x<n&&p.y>=0&&p.y<m&&v[p.x][p.y]==0)
            {
                if(mp[p.x][p.y]=='@'||mp[p.x][p.y]=='a')
                {
                    r.push(point(p.x,p.y,u.step+1,0));
                }
                if(mp[p.x][p.y]=='x')
                {
                    r.push(point(p.x,p.y,u.step+1,1));
                }
                v[p.x][p.y]=1;
            }
        }
    }
    return 0;
}
int main()
{
    int t;

    cin>>t;
    while(t--)
    {
        cin>>n>>m;
        for(int i=0;i<n;i++)
        {
            scanf("%s",mp[i]);
        }

        for(int i=0;i<n;i++)
        {
            for(int j=0;j<m;j++)
            {
                v[i][j]=0;
                if(mp[i][j]=='r')
                {
                    xr=i;
                    yr=j;
                    v[i][j]=1;
                }
            }
        }
        int ans=Bfs();
        if(ans==0)
            printf("Impossible\n");
        else{
            printf("%d\n",ans);
        }
    }
    return 0;
}

二、

百炼4115 鸣人与佐助(OpenJudge - 4115:鸣人和佐助

        先列出一个图简单说明与上一题的区别

 如果查克拉数量是3,如果本题不重复杀死其他路径杀死的蛇那么某些情况将无解,所以本题和上题的区别就是要重复走其他路径走过的路。明白了这一点就好做了,记录是否被访问用一个三维数组,不光记录坐标还要记录当前查克拉的数量,只有当查克拉数量相同并且当前点被访问过才不需要重复访问(因为查克拉相同说明击杀的蛇数量相同,被访问过说明有一条比当前更优的路)

理解了这些这一题就基本出来了,下面放出ac代码

#include<iostream>
#include<queue>
using namespace std;
char mp[210][210];
int v[210][210][20];
int Dx[4]={0,1,0,-1};
int Dy[4]={1,0,-1,0};
int m,n,k;
int xr,yr,xe,ye;
struct point{
    int x,y,t,k;
    point(int a,int b,int c,int d):x(a),y(b),t(c),k(d){}
    point(){}
};
queue<point> q;
struct point u;
int bfs()
{
    v[xr][yr][k]=1;
    q.push(point(xr,yr,0,k));
    while(!q.empty())
    {
        u=q.front();
        q.pop();
        if(u.x==xe&&u.y==ye)
            return u.t;
        for(int i=0;i<4;i++)
        {
            int nx=u.x+Dx[i];
            int ny=u.y+Dy[i];
            if(nx<0||ny<0||nx>=m||ny>=n)
                continue;
            if(mp[nx][ny]=='#'&&v[nx][ny][u.k-1]==0&&u.k>0)//遇到敌人而且在当前剩余差拉克数量相同的时候当前走的路径最短
            {
                v[nx][ny][u.k-1]=1;
                q.push(point(nx,ny,u.t+1,u.k-1));
            }
            else if(mp[nx][ny]!='#'&&v[nx][ny][u.k]==0)//点前k数量相同的时候如果已经走过的路,先走的最短,就没有必要走了
            {
                v[nx][ny][u.k]=1;
                q.push(point(nx,ny,u.t+1,u.k));
            }
        }
    }
    return 0;
}
int main()
{
    while(cin>>m>>n>>k)
    {
        for(int i=0;i<m;i++)
            for(int j=0;j<n;j++)
            {
                char c;
                cin>>c;
                mp[i][j]=c;
                if(c=='@')
                {
                    xr=i;
                    yr=j;
                }
                if(c=='+')
                {
                    xe=i;
                    ye=j;
                }
            }
        int ans=bfs();
        if(ans)
            printf("%d\n",ans);
        else
            printf("-1\n");

    }

    return 0;
}

三、

百炼4130 Saving Tang Monk(OpenJudge - 4130:Saving Tang Monk

        这一题就和上一题就很相似了,这一题的钥匙的作用就相当于查克拉,但是比上一题多一个找钥匙的步骤,钥匙只能找比当前获得的钥匙编号大1的钥匙,还有一个就是重复访问钥匙房间,比如当前找到了钥匙2,那么就不能继续访问其他路径访问过的钥匙2房间,因为另一条路一定比当前路径更短(因为先找到2钥匙),所以这里处理和上一题是一样的用一个三维数组来记录坐标和钥匙编号。但是这一题的难点就是在找钥匙的时候同一条路径是可以来回走,因为钥匙是按顺序来找的,这时对杀蛇怎么处理。例如在当前路径上需要先去杀蛇拿到钥匙1再回来拿钥匙2,就需要两次经过同一个有蛇的房间,这时就需要钥匙的三维数组和蛇的状态同时配合使用来标记一条路径上蓑鲉蛇的状态,这里用到了状态压缩,用二进制数来表示每一条蛇,如下代码

if(isalpha(mp[nx][ny]))
{
    bool killed=(1<<(mp[nx][ny]-'A'))&snack;
    int ns=snack+(1<<(mp[nx][ny]-'A'));
	if(killed&&!v[nx][ny][key]) {
        Q.push(point(nx,ny,time+1,key,snack));
        v[nx][ny][key]=1;
    }
    else if(!killed&&!v[nx][ny][key]) {
        v[nx][ny][key]=1;
        Q.push(point(nx,ny,time+2,key,ns));
    }
}

比如下一个点的蛇没有被杀的时候将当前蛇(假设是遇到的第一条蛇)设置(利用int ns=snack+(1<<(mp[nx][ny]-'A'));)为0001,第二条蛇0011以此类推,再用蛇的编号进行&运算得出是否被杀死。当然当key相同而且在另一条路径上该蛇已经被杀死了,那么当前路径就没有必要重复了(因为另一条路径一定更短)下面给出ac代码(参考了这篇博客)特此感谢

#include<iostream>
#include<cstdio>
#include<queue>
#include<string.h>
using namespace std;
int n,k;
int xk,yk,xt,yt;
char mp[110][110];
int v[110][100][10];
int Dx[4]={0,1,0,-1};
int Dy[4]={1,0,-1,0};
struct point{
    int x,y,time,key,snack;
    point(int a,int b,int c,int d,int e):x(a),y(b),time(c),key(d),snack(e){}
    bool operator < (const point& p) const{
        return time>p.time;
    }
};
int bfs(int xk,int yk)
{
    priority_queue <point> Q;
    Q.push(point(xk,yk,0,0,0));
    v[xk][yk][0]=1;
    while(!Q.empty())
    {
        int x=Q.top().x, y=Q.top().y, time=Q.top().time, key=Q.top().key,snack=Q.top().snack;
        Q.pop();
        if(key==k&&x==xt&&y==yt)
            return time;
        for(int i=0;i<4;i++)
        {
            int nx=x+Dx[i];
            int ny=y+Dy[i];
            if(nx<0||ny<0||nx>=n||ny>=n||mp[nx][ny]=='#')
                continue;
            if(isalpha(mp[nx][ny]))
            {
                bool killed=(1<<(mp[nx][ny]-'A'))&snack;
				int ns=snack+(1<<(mp[nx][ny]-'A'));
				if(killed&&!v[nx][ny][key]) {
					Q.push(point(nx,ny,time+1,key,snack));
					v[nx][ny][key]=1;
				}
				else if(!killed&&!v[nx][ny][key]) {
					v[nx][ny][key]=1;
					Q.push(point(nx,ny,time+2,key,ns));
				}
            }
            else
            {
                if(v[nx][ny][key]==0)
                {
                    v[nx][ny][key]=1;
                    Q.push(point(nx,ny,time+1,key,snack));
                }
                if(isdigit(mp[nx][ny]))
                {
                    int nkey = mp[nx][ny]-'0';
                    if(nkey==key+1&&v[nx][ny][nkey]==0)
                    {
                        Q.push(point(nx,ny,time+1,nkey,snack));
                        v[nx][ny][nkey]=1;
                    }
                }
            }
        }
    }
    return -1;
}
int main()
{
    while(cin>>n>>k)
    {
        if(n==0)
            break;
        int snake=0;
        for(int i=0;i<n;i++)
        {
            scanf("%s",mp[i]);
        }
        for(int i=0;i<n;i++)
            for(int j=0;j<n;j++)
            {
                if(mp[i][j]=='K')
                {
                    mp[i][j]='.';
                    xk=i;
                    yk=j;
                }else if(mp[i][j]=='T')
                {
                    mp[i][j]='.';
                    xt=i;
                    yt=j;
                }else if(mp[i][j]=='S')
                {
                    mp[i][j]='A'+snake++;
                }
            }
        memset(v,0,sizeof(v));
        int ans=bfs(xk,yk);
		if(ans==-1) cout<<"impossible"<<endl;
		else cout<<ans<<endl;
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值