UVA 1601 The Morning after Halloween (bfs好题)

题目链接

题意:最多三个字母,要让他们移动到对应的大写字母的方格里,问最小的步数(一步里面三个字母可以同时走)
解析:直接bfs做的话肯定会超时,因为每一步都是五的三次方,而且障碍物很多,所以我们需要建图,听起来挺可怕的,其实不然,建图就是我们将图中可以走的节点进行标号,然后每个节点i可以到达的节点j记录到对应的二维数组G[i][deg[i]++]]中,这样就建好图了,这样做本来两个坐标表示的一个点也可以用标号直接表示,然后进行bfs就可以了,注意一个细节就是两个点的位置是不可以互换的,也就是不能对着走.
代码中也有详细注释

#include<bits/stdc++.h>
#define ll long long
#define inf 0x3f3f3f3f
#define pb push_back
#define rep(i,a,b) for(int i=a;i<b;i++)
#define rep1(i,b,a) for(int i=b;i>=a;i--)
using namespace std;
const int maxn=150,maxs=20;
const int dx[]={-1,1,0,0,0};
const int dy[]={0,0,1,-1,0};
int w,h,n;
struct node
{
    int a,b,c;
};
bool iswell(int x,int y)
{
    if(x>=0&&y>=0&&y<w&&x<h)
        return 1;
    return 0;
}
int s[3],t[3]; //保存初末状态
int deg[maxn];//某个格子有多少个相连的格子
int G[maxn][5];//保存某个格子可以用到哪些格子
bool conflict(int a,int b,int a2,int b2)
{
    return a2==b2||(a2==b&&b2==a); //如果两个鬼移动到同一个位置或者位置互换则冲突
}
int d[maxn][maxn][maxn]; //走到某个状态经过的步数
int bfs()
{
    queue<node> q;
    memset(d,-1,sizeof(d));
    node t1={s[0],s[1],s[2]};
    q.push(t1); //每个状态给他编号来判断是否访问过
    d[s[0]][s[1]][s[2]]=0;
    while(!q.empty())
    {
        node u=q.front();
        q.pop();
        int a=u.a,b=u.b,c=u.c;
        if(a==t[0]&&b==t[1]&&c==t[2])return d[a][b][c];
        for(int i=0;i<deg[a];i++){
            int a2=G[a][i];
            for(int j=0;j<deg[b];j++){
                int b2=G[b][j];
                if(conflict(a,b,a2,b2))continue;
                for(int k=0;k<deg[c];k++){
                    int c2=G[c][k];
                    if(conflict(a,c,a2,c2))continue;
                    if(conflict(b,c,b2,c2))continue;
                    if(d[a2][b2][c2]!=-1)continue;
                    d[a2][b2][c2]=d[a][b][c]+1;
                    node tt={a2,b2,c2};
                    q.push(tt);
                }
            }
        }
    }
    return -1;
}
int main()
{

    while(cin>>w>>h>>n&&n)
    {
        getchar();
        string maze[20];
        for(int i=0;i<h;i++)
            getline(cin,maze[i]);

        int cnt,x[maxn],y[maxn],id[maxn][maxn];
        cnt=0;
        for(int i=0;i<h;i++)
        {
            for(int j=0;j<w;j++)
            {
                if(maze[i][j]!='#')
                {  //把障碍去掉,建图,也就是为每个节点编号
                    x[cnt]=i,y[cnt]=j,id[i][j]=cnt;//id数组用于下面找相邻的空格
                    if(islower(maze[i][j]))
                        s[maze[i][j]-'a']=cnt;
                    else if(isupper(maze[i][j]))
                        t[maze[i][j]-'A']=cnt;
                    cnt++;
                }
            }
        }
        for(int i=0;i<cnt;i++)
        {
            deg[i]=0;
            for(int dir=0;dir<5;dir++)
            {
                int nx=x[i]+dx[dir],ny=y[i]+dy[dir];
                if(iswell(nx,ny)&&maze[nx][ny]!='#')
                    G[i][deg[i]++]=id[nx][ny];//统计出每个位置可以移动到的位置
            }
        }
         //n==1||n==2时,把格子加上,凑够三个,初末位置重合,便于操作,不用分情况考虑
        if(n<=2){
            deg[cnt]=1;G[cnt][0]=cnt;s[2]=t[2]=cnt++;
        }
        if(n<=1){
            deg[cnt]=1;G[cnt][0]=cnt;s[1]=t[1]=cnt++;
        }
        cout<<bfs()<<endl;

    }
}
posted @ 2018-04-11 18:20  ffgcc  阅读(111)  评论(0编辑  收藏  举报