1601 - The Morning after Halloween

You are workingfor an amusement park as an operator of an obakeyashiki, or a hauntedhouse, in which guests walk through narrow and dark corridors. The house isproud of their lively ghosts, which are actually robots remotely controlled bythe operator, hiding here and there in the corridors. One morning, you foundthat the ghosts are not in the positions where they are supposed to be. Ah,yesterday was Halloween. Believe or not, paranormal spirits have moved themaround the corridors in the night. You have to move them into their rightpositions before guests come. Your manager is eager to know how long it takesto restore the ghosts.

In this problem,you are asked to write a program that, given a floor map of a house, finds thesmallest number of steps to move all ghosts to the positions where they aresupposed to be.

A floor consistsof a matrix of square cells. A cell is either a wall cell where ghosts cannotmove into or a corridor cell where they can.

At each step, youcan move any number of ghosts simultaneously. Every ghost can either stay inthe current cell, or move to one of the corridor cells in its 4-neighborhood(i.e. immediately left, right, up or down), if the ghosts satisfy the followingconditions:

 

  1. No more than one ghost occupies one position at the end of the step.
  2. No pair of ghosts exchange their positions one another in the step.


For example, suppose ghosts are located as shown in the following (partial)map, where a sharp sign (`#') represents a wall cell and `a', `b', and `c'ghosts.

 

                                  ####

                                   ab#

                                  #c##

                                  ####


The following four maps show the only possible positions of the ghosts afterone step.

 

                       ####   ####  ####   ####

                        ab#   a b#  acb#   ab #

                       #c##   #c##  # ##   #c##

                       ####   ####  ####   ####

Input 

The input consistsof at most 10 datasets, each of which represents a floor map of a house. Theformat of a dataset is as follows.

 

w

h

n

 

c11

c12

...

c1w

c21

c22

...

c2w

ch1

ch2

...

chw


w , h and n in the first line areintegers, separated by a space. w and h arethe floor width and height of the house, respectively. n isthe number of ghosts. They satisfy the following constraints.

4w16,  4h16,  1n3

Subsequent h linesof w characters are the floor map. Each of cij iseither:

 

  • a `#' representing a wall cell,
  • a lowercase letter representing a corridor cell which is the initial position of a ghost,
  • an uppercase letter representing a corridor cell which is the position where the ghost corresponding to its lowercase letter is supposed to be, or
  • a space representing a corridor cell that is none of the above.

In each map, eachof the first n letters from a and the first n lettersfrom A appears once and only once. Outermost cells of a map are walls; i.e. allcharacters of the first and last lines are sharps; and the first and lastcharacters on each line are also sharps. All corridor cells in a map areconnected; i.e. given a corridor cell, you can reach any other corridor cell byfollowing corridor cells in the 4-neighborhoods. Similarly, all wall cells areconnected. Any 2×2 area on any map has at least one sharp. You canassume that every map has a sequence of moves of ghosts that restores allghosts to the positions where they are supposed to be.

The last datasetis followed by a line containing three zeros separated by a space.

Output 

For each datasetin the input, one line containing the smallest number of steps to restoreghosts into the positions where they are supposed to be should be output. Anoutput line should not contain extra characters such as spaces.

Sample Input 

5 5 2

#####

#A#B#

#   #

#b#a#

#####

16 4 3

################

## ########## ##

#    ABCcba   #

################

16 16 3

################

### ##    #  ##

##  # ##   # c#

#  ## ########b#

# ##  # #  #  #

#  # ##  # # ##

##  a#  # ##  #

### ## #### ## #

##   #  #  #  #

#  ##### # ## ##

####   #B# #  #

##  C#  #   ###

#  # # ####### #

# ######  A##  #

#        #   ##

################

0 0 0

Sample Output 

7

36

77

代码:

整体思路:

优化转移代价:条件“任何一个2*2的子网格中至少有一个格子是障碍格”说明很多格子都是障碍格,并且大部分空地都和障碍格相邻因此,不是所有的四个方向都能移动,因此将所有的空格提取出来建立一张图,而不是每次临时判断5种方案是否合法。

本题目还可以使用“双向广度优先搜索”。

#include<cstdio>

#include<cstring>

#include<cctype>

#include<queue>

using namespacestd;

 

const int maxs =20; //最大的维数

const int maxn =150;

/*

题目中说的任何一个2*2的子网格中至少有一个格子是障碍格”,因此,空格的最大数目占所有各自的75%

*/

const int dx[]={1,-1,0,0,0}; // 4个移动方向+不动,共5种行动方案

const int dy[]={0,0,1,-1,0};

 

inline int ID(inta, int b, int c)

{

    return (a<<16)|(b<<8)|c;

/*

对于每一个不同的状态,根据三个鬼(有人为添加的,凑够三个)所在的空格的编号映射到唯一的数字然后插入到队列中进行BFS

*/

}

 

int s[3], t[3];

/*

s数组:三个鬼的所在的初始空格的编号;t数组:三个鬼的所在的目标空格的编号;

*/

int deg[maxn],G[maxn][5]; 

/*

deg数组存储对于每一个空格5中行动方案中可行的个数,来作为BFS的上限

G数组存储每一个空格可行的下一个空格的编号

*/

 

inline boolconflict(int a, int b, int a2, int b2)

//判断两个鬼移动是否会发生冲突:移动到同一个格子上或者互换了位置

{

    return a2 == b2 || (a2 == b && b2== a);

}

 

intd[maxn][maxn][maxn]; 

/*

存储每一次移动后的状态(三个维度分别存储三个鬼所在的新的空格下标,表示一个新状态)距离初始状态的移动步数

*/

 

int bfs() //宽搜

{

    queue<int> q;

    memset(d, -1, sizeof(d)); //每一组都要清空数据

    q.push(ID(s[0], s[1], s[2])); //  插入开始的节点

    d[s[0]][s[1]][s[2]] = 0;

    while(!q.empty())

    {

        int u = q.front();

        q.pop();

        int a = (u>>16)&0xff, b =(u>>8)&0xff, c = u&0xff;

// 还原当前状态的三个鬼所在的空格编号

        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;

/*

移动不冲突并且是新状态,步数+1。所谓的同一步可以移动任意多个鬼,就是说步数增量为1的状态可以是很多个,使用三重循环,使得对于当前的三个鬼任意其中多个鬼移动一步后的结果都是步数+1,即实现了“同一步可以移动任意多个鬼”。

*/

                    q.push(ID(a2, b2, c2));

//将新状态的唯一映射的数字插入队列

                }

            }

        }

    }

    return -1;//找不到返回-1

}

int main()

{

    int w, h, n;

    while(scanf("%d%d%d\n", &w,&h, &n) == 3 && n)

    {

        char maze[20][20];

        for(int i = 0; i < h; i++)

        {

            fgets(maze[i], 20, stdin);

        }

/*

        提取出空格:

        cnt是空格的下标,

        x[cnt]:是第(cnt+1)个空格在迷宫中的行下标,

        y[cnt]是第(cnt+1)个空格在迷宫中的行下标,

        id[i][j]的值是在迷宫中位置为maze[i][j] 的空格的编号

        int cnt, x[maxn], y[maxn],id[maxs][maxs];  

        cnt = 0;空格从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;

                    if(islower(maze[i][j]))// 出发点

                    {

                        s[maze[i][j] - 'a'] =cnt;

//题意为小写字母从a开始且连续

                    }

                    elseif(isupper(maze[i][j]))

                    {

                        t[maze[i][j] - 'A'] =cnt;

                    }

                    cnt++;//空格数增加1

                }

            }

        }

        //为空格建图:

        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];

//”Outermost cellsof a map are walls"意味着我们不需要检查空格的四周是否越界

                if(maze[nx][ny] != '#')

                {

                    G[i][deg[i]++] =id[nx][ny];

// G数组存储每一个空格可行的下一个空格的编号

                }

            }

        }

        //添加假的鬼使得每一组的鬼的数目都是3,从而简化BFS的代码

        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++;

        }

        printf("%d\n", bfs());

    }

    return 0;

}

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

#include<iostream>

#include<cstring>

#include<cctype>

#include<queue>

#include<string>

using namespacestd;

 

int w,h,n;

string str[20];

intstart[3],close[3];

intdeg[150],G[150][5];

int dx[]={0,-1,1,0,0};

int dy[]={0,0,0,-1,1};

intd[150][150][150];

 

int bfs();

bool conflict(inta,int b,int a2,int b2);

int ID(int a,intb,int c);

 

int main()

{

   while(cin>>w>>h>>n&&n)

    {

        for(int i=0; i<h; i++)

        {

            getline(cin,str[i]);

        }

        int cnt=0,x[150],y[150],id[20][20];

        for(int i=0; i<h; i++)

        {

            for(int j=0; j<w; j++)

            {

                if(str[i][j]!='#')

                {

                    x[cnt]=i;

                    y[cnt]=j;

                    id[i][j]=cnt;

                    if(islower(str[i][j]))

                    {

                       start[str[i][j]-'a']=cnt;

                    }

                    else if(isupper(str[i][j]))

                    {

                       close[str[i][j]-'A']=cnt;

                    }

                    cnt++;

                }

            }

        }

        for(int i=0; i<cnt; i++)

        {

            deg[i]=0;

            for(int dir=0; dir<5; dir++)

            {

                int newx=x[i]+dx[dir];

                int newy=y[i]+dy[dir];

                if(str[newx][newy]!='#')

                {

                   G[i][deg[i]++]=id[newx][newy];

                }

            }

        }

        if(n<=2)

        {

            deg[cnt]=1;

            G[cnt][0]=cnt;

            start[2]=close[2]=cnt++;

        }

        if(n<=1)

        {

            deg[cnt]=1;

            G[cnt][0]=cnt;

            start[1]=close[1]=cnt++;

        }

        cout<<bfs()<<endl;

    }

}

 

int bfs()

{

    queue<int> q;

    q.push(ID(start[0],start[1],start[2]));

    memset(d,-1,sizeof(d));

    d[start[0]][start[1]][start[2]]=0;

    while(!q.empty())

    {

        int u=q.front();

        q.pop();

        inta=(u>>16)&0xff,b=(u>>8)&0xff,c=u&0xff;

       if(a==close[0]&&b==close[1]&&c==close[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(b,c,b2,c2))

                    {

                        continue;

                    }

                    if(conflict(a,c,a2,c2))

                    {

                        continue;

                    }

                    if(d[a2][b2][c2]!=-1)

                    {

                        continue;

                    }

                    d[a2][b2][c2]=d[a][b][c]+1;

                    q.push(ID(a2,b2,c2));

                }

            }

        }

    }

    return -1;

}

 

int ID(int a,intb,int c)

{

    return (a<<16)|(b<<8)|c;

}

 

boolconflict(int  a,int b,int a2,int b2)

{

    return a2==b2||(a==b2&&b==a2);

}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值