UVALive 5966 Blade and Sword -- 搜索(中等题)

题意:给一幅地图,P为起点,D为终点,'*'为传送阵,到达传送阵可以传到任意一个其他的传送阵,传到以后可以正常走或者再传回来,问P->D最短步数。

分析:这题一定要细心,分析要到位才能搞定,错一点都WA。有两种思路:

1.走到一个传送点之后,将所有能传到的地方都加入队列,然后清除传送阵向量(vector),因为以后不用再互相传了,对于某个传送阵k,以后从别的点传到k的效果还不如从这个点传到k,所以清空传送阵,又因为其他传送阵可以传回来,此时传送阵向量要把当前传送阵push_back进去,以便其他点传过来,每个传送点设一个标记,flag=0说明这个传送点还没用过,flag=1说明这个传送点是被传过来的,还可以传一次,use[][]数组记录某个传送点被用过没有(即通过它传出去过没有),然后搞即可。

代码:(63ms, 0KB)

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <vector>
#include <queue>
#define Mod 1000000007
using namespace std;
#define N 1000017

char ss[205][205];
bool vis[205][205],use[205][205];
int n,m,step;
int dx[4] = {0,0,1,-1};
int dy[4] = {1,-1,0,0};
struct node
{
    int x,y,tag;
    node(int _x,int _y,int _tag)
    {
        x = _x;
        y = _y;
        tag = _tag;
    }
    node(){}
}P,D;
vector<node> trans;

bool OK(int nx,int ny)
{
    if(nx >= 0 && nx < n && ny >= 0 && ny < m)
        return 1;
    return 0;
}

bool bfs()
{
    queue<node> que;
    memset(vis,0,sizeof(vis));
    memset(use,0,sizeof(use));
    que.push(P);
    int i,j,k;
    vis[P.x][P.y] = 1;
    while(!que.empty())
    {
        int SIZE = que.size();
        while(SIZE--)
        {
            node tmp = que.front();
            que.pop();
            int nx = tmp.x;
            int ny = tmp.y;
            if(nx == D.x && ny == D.y)
                return 1;
            if(ss[nx][ny] == '*')   //tag = 1 : 被传过来的,tag = 0 : 传出去
            {
                if(tmp.tag == 0 || (tmp.tag==1 && !use[nx][ny]))
                {
                    for(i=0;i<trans.size();i++)
                    {
                        int x = trans[i].x;
                        int y = trans[i].y;
                        if(vis[x][y] || (nx == x && ny == y))
                            continue;
                        trans[i].tag = 1;
                        que.push(trans[i]);
                    }
                    trans.clear();
                    trans.push_back(tmp);  //这个点还可以被传回来
                    if(tmp.tag == 1)   //被传过来并且没用过的,现在用过了
                        vis[nx][ny] = 1;
                    use[nx][ny] = 1;
                }
                if(tmp.tag == 1)   //被传,用过了,就当做普通点好了  别用else if
                {
                    for(k=0;k<4;k++)
                    {
                        int kx = nx + dx[k];
                        int ky = ny + dy[k];
                        if(!OK(kx,ky) || ss[kx][ky] == '#' || ss[kx][ky] == '*' || vis[kx][ky])
                            continue;
                        vis[kx][ky] = 1;
                        que.push(node(kx,ky,0));
                    }
                    vis[nx][ny] = 1;
                }
            }
            else   //普通点,普通走法
            {
                for(k=0;k<4;k++)
                {
                    int kx = nx + dx[k];
                    int ky = ny + dy[k];
                    if(!OK(kx,ky) || ss[kx][ky] == '#' || vis[kx][ky])
                        continue;
                    if(ss[kx][ky] != '*' )  //如果是'*',则不能确定
                        vis[kx][ky] = 1;
                    que.push(node(kx,ky,0));
                }
            }
        }
        step++;
    }
    return 0;
}

int main()
{
    int t,cs = 1,i,j;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d%d",&n,&m);
        trans.clear();
        for(i=0;i<n;i++)
        {
            scanf("%s",ss[i]);
            for(j=0;j<m;j++)
            {
                if(ss[i][j] == '*')
                    trans.push_back(node(i,j,0));
                else if(ss[i][j] == 'P')
                    P = node(i,j,0);
                else if(ss[i][j] == 'D')
                    D = node(i,j,0);
            }
        }
        step = 0;
        if(bfs())
            printf("Case %d: %d
",cs++,step);
        else
            printf("Case %d: impossible
",cs++);
    }
    return 0;
}
View Code

2.统计传送点的数量,如果只有一个传送点,那么不能经过传送点到达,只能走普通路到终点。否则,先走一遍普通路ans = bfs(),再从P搜一个与他最近的传送点,从D搜一个与它最近的传送点,判断这两个传送点是不是同一个,如果是只能再通过图中另一个传送点作为终中继,此时ans = bfsP()+bfsD()+2,不是同一个那就更好处理了,ans = bfsP+bfsD+1。细节问题看代码吧。

代码:(22ms, 0KB)

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <queue>
#define Mod 1000000007
using namespace std;
#define N 1000017

struct node
{
    int x,y,step;
}P,D;

int n,m;
int cnt1,cnt2;
char ss[205][205];
int vis[205][205];
queue<node> que;
int dx[4] = {0,0,1,-1};
int dy[4] = {1,-1,0,0};
int X1,X2,Y1,Y2;

int OK(int nx,int ny)
{
    if(nx >= 0 && nx < n && ny >= 0 && ny < m && ss[nx][ny] == '.')
        return 1;
    return 0;
}

int bfsPD()
{
    while(!que.empty())
        que.pop();
    P.step = 0;
    memset(vis,0,sizeof(vis));
    que.push(P);
    vis[P.x][P.y] = 1;
    while(!que.empty())
    {
        node now = que.front();
        que.pop();
        int nx = now.x;
        int ny = now.y;
        int step = now.step;
        for(int k=0;k<4;k++)
        {
            int kx = nx + dx[k];
            int ky = ny + dy[k];
            if(ss[kx][ky] == 'D')
                return step+1;
            if(!OK(kx,ky) || vis[kx][ky])  //不考虑'*'
                continue;
            node tmp;
            tmp.x = kx;
            tmp.y = ky;
            tmp.step = step + 1;
            vis[kx][ky] = 1;
            que.push(tmp);
        }
    }
    return Mod;
}

int bfsP()    //从P找'*'
{
    int dis = Mod;
    while(!que.empty())
        que.pop();
    memset(vis,0,sizeof(vis));
    P.step = 0;
    que.push(P);
    vis[P.x][P.y] = 1;
    while(!que.empty())
    {
        node now = que.front();
        que.pop();
        int nx = now.x;
        int ny = now.y;
        int step = now.step;
        if(step >= dis)
            return dis;
        for(int k=0;k<4;k++)
        {
            int kx = nx + dx[k];
            int ky = ny + dy[k];
            if(ss[kx][ky] == '*')
            {
                X1 = kx;
                Y1 = ky;
                cnt1++;
                dis = step + 1;
            }
            else if(OK(kx,ky))
            {
                if(!vis[kx][ky])
                {
                    node tmp;
                    tmp.x = kx;
                    tmp.y = ky;
                    tmp.step = step+1;
                    vis[kx][ky] = 1;
                    que.push(tmp);
                }
            }
        }
    }
    return dis;
}

int bfsD()    //从D找'*'
{
    int dis = Mod;
    while(!que.empty())
        que.pop();
    memset(vis,0,sizeof(vis));
    D.step = 0;
    que.push(D);
    vis[D.x][D.y] = 1;
    while(!que.empty())
    {
        node now = que.front();
        que.pop();
        int nx = now.x;
        int ny = now.y;
        int step = now.step;
        if(step >= dis)
            return dis;
        for(int k=0;k<4;k++)
        {
            int kx = nx + dx[k];
            int ky = ny + dy[k];
            if(ss[kx][ky] == '*')
            {
                X2 = kx;
                Y2 = ky;
                cnt2++;
                dis = step + 1;
            }
            else if(OK(kx,ky))
            {
                if(!vis[kx][ky])
                {
                    node tmp;
                    tmp.x = kx;
                    tmp.y = ky;
                    tmp.step = step+1;
                    vis[kx][ky] = 1;
                    que.push(tmp);
                }
            }
        }
    }
    return dis;
}

int main()
{
    int t,cs = 1,i,j;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d%d",&n,&m);
        int cnt = 0;
        for(i=0;i<n;i++)
        {
            scanf("%s",ss[i]);
            for(j=0;j<m;j++)
            {
                if(ss[i][j] == 'P')
                    P.x = i,P.y = j;
                else if(ss[i][j] == 'D')
                    D.x = i,D.y = j;
                else if(ss[i][j] == '*')
                    cnt++;
            }
        }
        int ans = 0;
        cnt1 = cnt2 = 0;
        if(cnt <= 1)    //少于等于1个传送点,就只能按普通路走过去
            ans = bfsPD();
        else
        {
            int d1,d2;
            ans = bfsPD();
            d1 = bfsP();
            d2 = bfsD();
            if(cnt1 == cnt2 && cnt1 == 1)  //周围都只有一个点,通过这两个点中介
            {
                if(X1 == X2 && Y1 == Y2)  //是同一个点,因为至少还有一个点,所以可以传回来
                    ans = min(ans,d1+d2+2);
                else
                    ans = min(ans,d1+d2+1);
            }
            else  //有不止一个点,可以用不同点传
                ans = min(ans,d1+d2+1);
        }
        if(ans >= Mod)
            printf("Case %d: impossible
",cs++);
        else
            printf("Case %d: %d
",cs++,ans);
    }
    return 0;
}
View Code

Written by Whatbeg

原文地址:https://www.cnblogs.com/whatbeg/p/3862437.html