• UVA


    题目:

    w*h(w,h≤16)网格上有n(n≤3)个小写字母(代表鬼)。要求把它们分别移动到对应的大写字母里。每步可以有多个鬼同时移动(均为往上下左右4个方向之一移动),但每步结束之后任何两个鬼不能占用同一个位置,也不能在一步之内交换位置。输入保证所有空格连通,所有障碍格也连通,且任何一个2*2子网格中至少有一个障碍格。输出最少的步数。

    分析:

    1.又碰到了“状态”这个很玄乎的词了。以当前三个鬼的位置为状态,那就有256^3(16*16=256)个,然后三个鬼在每一个状态中的移动有5^3个,这时间和空间肯定受不了啊。

    2.紫书上说,可以将空格提取出来,然后对其BFS就可以了……读完有些懵逼提取?那还不是遍历?看完代码才明白可以对每个空格子进行编号,然后存一下每个格子可以走向的格子的编号就提取出来了!!其中还对每一个状态进行了编号,这也太妙了!!还是太菜了……之后BFS就可以了。

    单向BFS代码:

    #include <bits/stdc++.h>
    #define inf 0x3f3f3f3f
    #define MAX 1000000009
    #define FRE() freopen("in.txt","r",stdin)
    #define FRO() freopen("out.txt","w",stdout)
    using namespace std;
    typedef long long ll;
    const int maxn = 150;
    const int dx[] = {-1,1,0,0,0};
    const int dy[] = {0,0,-1,1,0};
    inline int ID(int a,int b,int c) {
        return (a<<16)|(b<<8)|c;
    }
    int s[3],t[3];//保存初末状态
    int deg[maxn];//某个格子有多少个与之相连的格子
    int G[maxn][5];//保存某个格子可以去向哪些格子
    inline 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<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;//记录距离
                        q.push(ID(a2,b2,c2));
                    }
                }
            }
        }
        return -1;
    }
    
    int main() {
        //FRE();
        int w,h,n;
        while(scanf("%d%d%d
    ",&w,&h,&n)==3 && n){
            char maze[20][20];
            for(int i=0; i<h; i++){
                fgets(maze[i],20,stdin);
            }
            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;//记录格子并编号
                        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(maze[nx][ny]!='#')
                        G[i][deg[i]++] = id[nx][ny];
                }
            }
            //如果起始格子不够3个就补全,让他们的起始位置和末位置重合就可以了
            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
    ",bfs());
        }
        return 0;
    }

     这个题目也可以用双向BFS来做,做完这个题,对双向BFS有了进一步的理解。那这里的正向就是遍历小写字母,反向就是遍历大写字母。当小写字母和大写字母的位置重合之后,就是得到的答案。

    双向BFS代码:

    #include <bits/stdc++.h>
    #define inf 0x3f3f3f3f
    #define MAX 1000000009
    #define FRE() freopen("in.txt","r",stdin)
    #define FRO() freopen("out.txt","w",stdout)
    using namespace std;
    typedef long long ll;
    const int maxn = 150;
    const int dx[] = {-1, 1, 0, 0, 0};
    const int dy[] = {0, 0, -1, 1, 0};
    inline int ID(int a, int b, int c) {
        return (a << 16) | (b << 8) | c;
    }
    int s[3], t[3]; //保存初末状态
    int deg[maxn];//某个格子有多少个与之相连的格子
    int G[maxn][5];//保存某个格子可以去向哪些格子
    inline bool conflict(int a, int b, int a2, int b2) {
        return a2 == b2 || (a2 == b && b2 == a); //如果两个鬼移动到同一个位置或位置互换则冲突
    }
    int d[maxn][maxn][maxn], vis[maxn][maxn][maxn]; //走到某个状态经过的步数
    
    int bfs() {
        queue<int> que,que_rev;
        d[s[0]][s[1]][s[2]] = 0;
        d[t[0]][t[1]][t[2]] = 0;
    
        que.push(ID(s[0],s[1],s[2]));
        que_rev.push(ID(t[0],t[1],t[2]));
    
        vis[s[0]][s[1]][s[2]] = 1;
        vis[t[0]][t[1]][t[2]] = 2;
    
        while(!que.empty() && !que_rev.empty()){
            int sza = que.size(),szb=que_rev.size();
            while(sza--){
                int u=que.front(); que.pop();
                int a=(u>>16)&0xff,b=(u>>8)&0xff,c=(u&0xff);
                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(vis[a2][b2][c2]==0){
                                vis[a2][b2][c2] = 1;
                                d[a2][b2][c2] = d[a][b][c]+1;
                                que.push(ID(a2,b2,c2));
                            }else if(vis[a2][b2][c2]==2){
                                return d[a2][b2][c2]+d[a][b][c]+1;
                            }
                        }
                    }
                }
            }
            while(szb--){
                int u=que_rev.front(); que_rev.pop();
                int a=(u>>16)&0xff, b=(u>>8)&0xff,c=(u&0xff);
                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(vis[a2][b2][c2]==0){
                                vis[a2][b2][c2]=2;
                                d[a2][b2][c2] = d[a][b][c]+1;
                                que_rev.push(ID(a2,b2,c2));
                            }else if(vis[a2][b2][c2]==1){
                                return d[a2][b2][c2]+d[a][b][c]+1;
                            }
                        }
                    }
                }
            }
        }
        return -1;
    }
    
    int main() {
        //FRE();
        int w, h, n;
        while(scanf("%d%d%d
    ", &w, &h, &n) == 3 && n) {
            char maze[20][20];
            memset(d, -1, sizeof(d));
            memset(vis, 0, sizeof(vis));
            int cnt, x[maxn], y[maxn], id[maxn][maxn];
            cnt = 0;
    
            for(int i = 0; i < h; i++) {
                fgets(maze[i], 20, stdin);
            }
    
            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;  //记录初始位置
                        } 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(maze[nx][ny] != '#')
                    { G[i][deg[i]++] = id[nx][ny]; }
                }
            }
            //如果起始格子不够3个就补全,让他们的起始位置和末位置重合就可以了
            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
    ", bfs());
        }
        return 0;
    }
  • 相关阅读:
    Anonymous Inner Class(匿名内部类)是否可以继承其它类?是否可以实现接口?
    IOC的优点是什么?
    《精益软件开发管理之道》阅读笔记02
    每日日报18
    每日日报17
    ecplise中没有Java Application的解决办法
    HTML+CSS+div 制作简单的登录界面
    HTML+CSS:通过li标签制作导航条
    每日日报16
    用Python爬取最新疫情数据(使用PyCharm)
  • 原文地址:https://www.cnblogs.com/sykline/p/10315229.html
Copyright © 2020-2023  润新知