UVA - 1601 The Morning after Halloween (bfs 单向/双向)

35 篇文章 0 订阅
17 篇文章 0 订阅

题目网址点击打开链接


引用一个学长的题解:

点击打开链接

出一个最大为16×16的迷宫图和至多3个ghost的起始位置和目标位置,求最少经过几轮移动可以使三个ghost都到达目标位置。每轮移动中,每个ghost可以走一步,也可以原地不动,需要注意的是任意两个ghost不能在相同的位置,因此也不能出现任意两个ghost对穿,也就是原来是ab,移动之后是ba。每个迷宫图'#'表示墙,' '表示空地,小写字母表示ghost的起始位置,大写字母表示对应ghost的目标位置,比如'a'应该走到'A'。保证任意2×2的空间内都有一个'#'。

  看起来就像是人数多了点的走迷宫嘛,BFS就是了。如果这么做的话果断就会超时,因为在每一个状态可以走的路太多,3个ghost的话,每个有5个方向可走,3个加起来除去原地不动还有124种走法,而且算出来的最少步数也不一定少,第三组样例的步数就多达77步,空间开不下,时间花得多。所以这道题就一定得优化了。

  首先是尽量避免搜索不合法的走法。这个时候就是题里的一个细节,保证任意2×2空间内都有一个‘#’,也就是说,能走的124步里面有相当多的情况是不合法的。每次都压到队列里面然后不合法再排除就浪费了很多时间,所以这就是优化的入口。这里用的办法是把迷宫图给转换成了图,用邻接表保存起来,这样搜索的时候只走可以走的点,省去了走‘#’再排除的时间。

  其次,在判重上也可以提高效率。实际上还是根据任意2×2都有'#'这个细节,可以粗略的估计出整个迷宫中可以走的空地不超过200个,3个ghost的话建一个三维数组vis,200×200×200=8000000,完全开得下。另外考虑ghost数量不同的问题,这里想到的方法是把不存在的多余的ghost放到一个孤立的点中,然后使其起始位置和目标位置相同即可,这样就避免了需要根据情况动态调整的麻烦。

  靠着上面两条,已经完全可以A过这题了。

  嗯,那么思路就是获得“输入→建图→BFS”了。

     其实这只是部分的优化而已,如果还要提高效率的话,就需要用到双向BFS了


单向bfs版本

 #include <cstdio>
#include <cstring>
#include <cctype>
#include <list>
#include <algorithm>
#include<queue>
#include<algorithm>
#include<iostream>

using namespace std;

int w, h, n;

char pic[20][20]; // 输入
int num[20][20]; // 输入中的位置→图中节点的编号
int vis[200][200][200]; // 标记数组
int connect[200][200]; // 邻接表
int all; // 图中节点的数量
struct node
{
    int a,b,c,stemp;
    node(int aa=0,int bb=0,int cc=0,int s=0)
    {
        a=aa;b=bb;c=cc;stemp=s;
    }
};
int que[4];
int goal[4]; // 目标状态

int BFS()
{
    // 初始化
    memset(vis, 0, sizeof(vis));
    queue<node> q;
    while(q.size())q.pop();

    int fro = 0, rear = 1;
    vis[que[1]][que[2]][que[3]] = true;
    q.push(node(que[1],que[2] ,que[3],0));
    while(q.size())
    {
        node s=q.front();q.pop();
        int step = s.stemp, a =s.a, b =s.b, c=s.c;
        if(a == goal[1] && b == goal[2] && c == goal[3])
        {
            return step;
        }

        for(int i = 0; i <= connect[a][0]; ++i)
       {
            int t1 = (i == 0 ? a : connect[a][i]);
            for(int j = 0; j <= connect[b][0]; ++j)
           {
                int t2 = (j == 0 ? b : connect[b][j]);
                for(int k = 0; k <= connect[c][0]; ++k)
               {
                    int t3 = (k == 0 ? c : connect[c][k]);
                    if((t1 && t2 && t1 == t2) || (t1 && t3 && t1 == t3) || (t2 && t3 && t2 == t3))
                         continue; // 不能重合
                    if(t1 && t2 && t1 == b && t2 == a)
                        continue; // t1,t2不能对穿
                    if(t1 && t3 && t1 == c && t3 == a)
                        continue; // t1,t3不能对穿
                    if(t2 && t3 && t2 == c && t3 == b)
                        continue; // t2,t3不能对穿
                    if(!vis[t1][t2][t3]) {
                        vis[t1][t2][t3] = 1;
                        q.push(node(t1,t2,t3,step+1));
                    }
                }
            }
        }
    }
    return 0;
}

int main()
{
    int _t = 0;
    while(scanf("%d%d%d", &w, &h, &n) && w && h && n)
    {
        memset(connect, 0, sizeof(connect));
        memset(num,0,sizeof(num));

        gets(pic[0]);
        for(int i = 0; i != h; ++i)
           gets(pic[i]);

        all = 0;
        // 获得编号
        for(int i = 0; i < h; ++i)
            for(int j = 0; j < w; ++j)
          {
             if(pic[i][j] != '#')
                num[i][j] = ++all;

         }
        // 建立图
        for(int i = 0; i < h; ++i)
           for(int j = 0; j < w; ++j)
            if(num[i][j])
           {
               int pos = num[i][j];
               if(num[i + 1][j]) connect[pos][++connect[pos][0]] = num[i + 1][j];
               if(num[i - 1][j]) connect[pos][++connect[pos][0]] = num[i - 1][j];
               if(num[i][j + 1]) connect[pos][++connect[pos][0]] = num[i][j + 1];
               if(num[i][j - 1]) connect[pos][++connect[pos][0]] = num[i][j - 1];
          }
        //初始化
        que[0] = que[1] = que[2] = que[3] = 0;
        goal[0] = goal[1] = goal[2] = goal[3] = 0;
        // 寻找初始状态
        for(int i = 0; i != h; ++i)
            for(int j = 0; j != w; ++j)
          {
               if(islower(pic[i][j]))
              {
                  if(pic[i][j] == 'a')
                     que[1] = num[i][j];
                  if(pic[i][j] == 'b')
                    que[2] = num[i][j];
                  if(pic[i][j] == 'c')
                    que[3] = num[i][j];
              }
               if(isupper(pic[i][j]))
              {
                if(pic[i][j] == 'A')
                  goal[1] = num[i][j];
                if(pic[i][j] == 'B')
                  goal[2] = num[i][j];
                if(pic[i][j] == 'C')
                  goal[3] = num[i][j];
              }
          }
        // 寻找目标状态

        int ans= BFS();

        printf("%d\n", ans);
    }
}

双向bfs版本(没找到好的,,,自己写的,,,写的也是很拖沓)(注意双向bfs是交替层搜索)

如果只是套两个bfs的话会错,就比如下面这种情况

但这种做法是有问题的,如下面的图:


求S-T的最短路,交替节点搜索(一次正向节点,一次反向节点)时

Step 1 : S –> 1 , 2

Step 2 : T –> 3 , 4

Step 3 : 1 –> 5

Step 4 : 3 –> 5   返回最短路为4,错误的,事实是3,S-2-4-T
#include <cstdio>
#include <cstring>
#include <cctype>
#include <list>
#include <algorithm>
#include<queue>
#include<algorithm>
#include<iostream>
#include<vector>

using namespace std;

int w, h, n;

char pic[20][20]; // 输入
int num[20][20]; // 输入中的位置→图中节点的编号
int vis[200][200][200]; // 标记数组
int feed[200][200][200];
int feed2[200][200][200];
int connect[200][200]; // 邻接表
int all; // 图中节点的数量
const int inf =0x3f3f3f3f;
struct node
{
    int a,b,c,stemp;
    node(int aa=0,int bb=0,int cc=0,int s=0)
    {
        a=aa;b=bb;c=cc;stemp=s;
    }
    bool operator<(const node &s)const
    {
        return stemp>s.stemp;
    }
};
bool cmp(node a,node b)
{
    return a.stemp>b.stemp;
}
int que[4];
int goal[4]; // 目标状态

int BFS()
{
    // 初始化
    memset(vis, 0, sizeof(vis));
    memset(feed,inf,sizeof(feed));
    memset(feed2,inf,sizeof(feed2));
    queue<node> q;
    queue<node> q2;
    while(q.size())q.pop();
    while(q2.size())q2.pop();

    int fro = 0, rear = 1;
    vis[que[1]][que[2]][que[3]] = 1;
    vis[goal[1]][goal[2]][goal[3]]=2;
    feed[que[1]][que[2]][que[3]] = 0;
    feed2[goal[1]][goal[2]][goal[3]]=0;
    q.push(node(que[1],que[2] ,que[3],0));
    q2.push(node(goal[1],goal[2],goal[3],0));
    int ans=inf;
    int v1,v2;
   // cout<<" !!!"<<connect[goal[3]][0]<<endl;
    while(q.size()||q2.size())
    {

        if(q.size())
        {  node s=q.front();
            v1=s.stemp;
        }
            while(q.size()){
            node s=q.front();
            int step = s.stemp, a =s.a, b =s.b, c=s.c;
            if(step!=v1)
                break;
            q.pop();
            if(vis[a][b][c]==2)
           {
             return feed[a][b][c]+feed2[a][b][c];
           }

           for(int i = 0; i <= connect[a][0]; ++i)
          {
              int t1 = (i == 0 ? a : connect[a][i]);
              for(int j = 0; j <= connect[b][0]; ++j)
             {
                  int t2 = (j == 0 ? b : connect[b][j]);
                  for(int k = 0; k <= connect[c][0]; ++k)
                 {
                    int t3 = (k == 0 ? c : connect[c][k]);
                    if((t1 && t2 && t1 == t2) || (t1 && t3 && t1 == t3) || (t2 && t3 && t2 == t3))
                         continue; // 不能重合
                    if(t1 && t2 && t1 == b && t2 == a)
                        continue; // t1,t2不能对穿
                    if(t1 && t3 && t1 == c && t3 == a)
                        continue; // t1,t3不能对穿
                    if(t2 && t3 && t2 == c && t3 == b)
                        continue; // t2,t3不能对穿
                    if(!vis[t1][t2][t3])
                    {
                        vis[t1][t2][t3]= 1;
                        q.push(node(t1,t2,t3,step+1));
                        feed[t1][t2][t3]=min(feed[t1][t2][t3],feed[a][b][c]+1);
                    }
                    if(vis[t1][t2][t3]==2)
                    {
                        return  feed2[t1][t2][t3]+feed[a][b][c]+1;
                    }
                }
              }
           }
         }
         if(q2.size())
         {  node s=q2.front();
             v2=s.stemp;
         }
          while(q2.size()){
            node s=q2.front();
            int step = s.stemp, a =s.a, b =s.b, c=s.c;
            //cout<<" 2a "<<a<<" 2b "<<b<<endl;
            if(step!=v2)
                break;
            q2.pop();
            if(vis[a][b][c]==1)
           {
              return feed[a][b][c]+feed2[a][b][c];
           }
           for(int i = 0; i <= connect[a][0]; ++i)
          {
              int t1 = (i == 0 ? a : connect[a][i]);
              for(int j = 0; j <= connect[b][0]; ++j)
             {
                  int t2 = (j == 0 ? b : connect[b][j]);
                  for(int k = 0; k <= connect[c][0]; ++k)
                 {
                    int t3 = (k == 0 ? c : connect[c][k]);
                    if((t1 && t2 && t1 == t2) || (t1 && t3 && t1 == t3) || (t2 && t3 && t2 == t3))
                         continue; // 不能重合
                    if(t1 && t2 && t1 == b && t2 == a)
                        continue; // t1,t2不能对穿
                    if(t1 && t3 && t1 == c && t3 == a)
                        continue; // t1,t3不能对穿
                    if(t2 && t3 && t2 == c && t3 == b)
                        continue; // t2,t3不能对穿
                    if(!vis[t1][t2][t3])
                    {
                        vis[t1][t2][t3] = 2;
                        q2.push(node(t1,t2,t3,step+1));
                        feed2[t1][t2][t3]=min(feed2[t1][t2][t3],feed2[a][b][c]+1);
                    }
                    if(vis[t1][t2][t3]==1)
                    {
                        return  feed[t1][t2][t3]+feed2[a][b][c]+1;
                    }
                }
              }
           }
         }

    }
  //  cout<<"never"<<endl;
    return ans;
}

int main()
{
    int _t = 0;
    while(scanf("%d%d%d", &w, &h, &n) && w && h && n)
    {
        memset(connect, 0, sizeof(connect));
        memset(num,0,sizeof(num));

        gets(pic[0]);
        for(int i = 0; i != h; ++i)
           gets(pic[i]);

        all = 0;
        // 获得编号
        for(int i = 0; i < h; ++i)
            for(int j = 0; j < w; ++j)
          {
             if(pic[i][j] != '#')
                num[i][j] = ++all;

         }
        // 建立图
        for(int i = 0; i < h; ++i)
           for(int j = 0; j < w; ++j)
            if(num[i][j])
           {
               int pos = num[i][j];
               if(num[i + 1][j]) connect[pos][++connect[pos][0]] = num[i + 1][j];
               if(num[i - 1][j]) connect[pos][++connect[pos][0]] = num[i - 1][j];
               if(num[i][j + 1]) connect[pos][++connect[pos][0]] = num[i][j + 1];
               if(num[i][j - 1]) connect[pos][++connect[pos][0]] = num[i][j - 1];
          }
        //初始化
        que[0] = que[1] = que[2] = que[3] = 0;
        goal[0] = goal[1] = goal[2] = goal[3] = 0;
        // 寻找初始状态
        for(int i = 0; i != h; ++i)
            for(int j = 0; j != w; ++j)
          {
               if(islower(pic[i][j]))
              {
                  if(pic[i][j] == 'a')
                     que[1] = num[i][j];
                  if(pic[i][j] == 'b')
                    que[2] = num[i][j];
                  if(pic[i][j] == 'c')
                    que[3] = num[i][j];
              }
               if(isupper(pic[i][j]))
              {
                if(pic[i][j] == 'A')
                  goal[1] = num[i][j];
                if(pic[i][j] == 'B')
                  goal[2] = num[i][j];
                if(pic[i][j] == 'C')
                  goal[3] = num[i][j];
              }
          }
        // 寻找目标状态

        int ans= BFS();
        if(ans==inf)
            ans=0;
        printf("%d\n", ans);
    }
}

这傻逼代码这么长,等以后再补一个好的双向bfs的板子,

今天玩了一天,中午回去领快递+睡觉,下午出去梦游骑车子了(日常雷雨天兴奋。。。)晚上回来才写的双向bfs版本

一天就写了个这个题,玩疯了真的是,,

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值