uva 10047

这是一道复合状态的迷宫搜索题 。
对vis数组的理解: 以前一直以为这只是一个标记每个点事否走过的数组 , 其实这只是片面的认识 , 它标记的不是一个点 , 而是一个“状态” , 状态和点是不一样的 , 点是单一的 , 而状态不只是表示一个点 , 更是表示这个点所处在的状态 。

这个题要把方向 , 颜色 , 点 ,一起看成一个综合的状态 , 那就意味着要定义一个四维数组 。

用bfs有两种方法 :
1、用优先队列 , 那么就可以减小很多内存的消耗 , 且速度也更快 。
用优先队列 , 可以不记录只改变方向点不变的状态 , 就是让这个点“改变方向+按这个方向走向下一个点”,一步完成 。如果这样的话 , 会产生一种情况 , 就是同一次bfs遍历到的点 , 所花的时间是不一样的。所以 , 如果按照这个情况bfs下去 , 并且取第一次到时间为最优时间,这样是有漏洞的 。

2、不用优先队列 , 但是只改变方向的状态也必须要记录 , 这样同时遍历到的点所花的时间就是一样的了 ,bfs最先找到的也就是最优的解 。

第一种情况的代码:
#include
#include
#include
#include

using namespace std ;

const int maxn = 25 + 10 ;
//一个状态的五个属性,坐标,方向,步数
struct Node { int x ; int y ; int d ; int s ; int c ; } node1 , node2 ;
//优先队列
struct cmp
{
      bool operator () (const Node a, const Node b)
      {
              return a.s > b.s;
      }
};

priority_queue ,cmp > q ;
//queue < Node > q ;
//一般队列不能用,因为队首可能不是最优的,因为当你得到一个状态扩展出来的所有状态时,
//你需要且只需要最优的那一个,所以优先队列成为必然的选择(当然你也可以在所有状态中找到当前最优的一个状态,嘿嘿)
char map[maxn][maxn] ;
bool visit[maxn][maxn][5][4] ;//标记状态数组
int r , c ;//行列
int bx , by , ex , ey ;//开始位置与终点位置
int ans ;//最小步数
int flag ;//标记是否有解

int dx[] = { -1 , 1 , 0 , 0 } ;//控制x方向
int dy[] = { 0 , 0 , -1 , 1 } ;//控制y方向
int dd[] = { 0 , 1 , 2 , 3 } ;//北,南,西,东(上,下,左,右)
//初始化
void init()
{
      bx = by = ex = ey = 0 ;
      ans = 0 ; flag = 0 ;
      while( ! q.empty() ) q.pop() ;
      memset( map , 0 , sizeof( map ) ) ;
      memset( visit , 0 , sizeof( visit ) ) ;
      for( int i = 0 ; i < r ; ++i )
      {
              for( int j = 0 ; j < c ; ++j )
              {
                      cin >> map[i][j] ;
                      if( map[i][j] == 'S' )
                      { bx = i ; by = j ; }
                      if( map[i][j] == 'T' )
                      { ex = i ; ey = j ; }
              }
      }
      return ;
}
//判断状态是否出现过
bool is_ok( int x , int y , int cc , int d )
{
      if( x < 0 || y < 0 || x >= r || y >= c || map[x][y] == '#' || visit[x][y][cc][d] )
      return false ;
      return true ;
}
//得到步数
int get_step( int nd , int d , int s )
{
      if( d == 0 && nd == 1 ) return s + 2 ;
      else if( d == 1 && nd == 0 ) return s + 2 ;
      else if( d == 2 && nd == 3 ) return s + 2 ;
      else if( d == 3 && nd == 2 ) return s + 2 ;
      else return s + 1 ;
}
//判断是否为解
bool is_ans( int x , int y , int cc )
{
      if( x != ex || y != ey || cc )
      return false ;
      return true ;
}
//广度优先搜索
void bfs()
{
      //初始化队首
      node1.x = bx , node1.y = by , node1.c = 0 , node1.s = 0 , node1.d = 0 ;
      q.push( node1 ) ;
      visit[bx][by][node1.c][node1.d] = true ;
      while( ! q.empty() )
      {
              node1 = q.top() ;
              q.pop() ;
              int x = node1.x ;
              int y = node1.y ;
              int c = node1.c ;
              int s = node1.s ;
              int d = node1.d ;
              for( int i = 0 ; i < 4 ; ++i )
              {
                      int nd = dd[i] ;
                      if( nd != d )//如果现在方向与走到下一个位置以后所具有的方向不同
                      {
                              int ns = get_step( nd , d , s ) ;
                              if( is_ans( x , y , c ) )
                              { ans = ns ; flag = 1 ; return ; }
                              if( is_ok( x , y , c , nd ) )
                              {
                                      node2.x = x ;
                                      node2.y = y ;
                                      node2.c = c ;
                                      node2.d = nd ;
                                      node2.s = ns ;// 走的时间
                                      q.push( node2 ) ;
                                      visit[x][y][c][nd] = true ;
                              }
                      }
                      if( nd == d )//如果方向相同,走一格
                      {
                              int ns = s + 1 ;
                              int nx = x + dx[d] ;
                              int ny = y + dy[d] ;
                              int nc = ( c + 1 ) % 5 ;//0 , 1 , 2 , 3 , 4 表示五个颜色
                              if( is_ok( nx , ny , nc , d ) )
                              {
                                      if( is_ans( nx , ny , nc ) )
                                      { ans = ns ; flag = 1 ; return ; }
                                      node2.x = nx ;
                                      node2.y = ny ;
                                      node2.c = nc ;
                                      node2.d = d ;
                                      node2.s = ns ;
                                      q.push( node2 ) ;
                                      visit[nx][ny][nc][d] = true ;
                              }
                      }
              }
      }
      return ;
}

int main()
{
      int icase = 1 ;
      while( cin >> r >> c )
      {
              if( ! r && ! c ) break ;
              init() ;
              if( icase - 1 ) cout << endl ;
              bfs() ;
              if( flag )
              cout << "Case #" << icase++ << endl << "minimum time = " << ans << " sec" << endl ;
              else
              cout << "Case #" << icase++ << endl << "destination not reachable" << endl ;
      }
      return 0 ;
}
第二种情况的代码:
#include
#include
#include
using namespace std;
int M[30][30][6][4];
char S[30][30];
int ix[4]={-1,0,1,0},a,b;
int iy[4]={0,1,0,-1};
int q1[20000],q2[20000],q3[20000],q4[20000];
int bfs(int i,int j)
{
      int rear=1,front=0;
      q1[0]=i,q2[0]=j,q3[0]=1,q4[0]=0;
      M[i][j][1][0]=1;
      int mm=0;
      while(front
      {
              int u=q1[front];
              int m=q2[front];
              int c=q3[front];
              int f=q4[front++];
              int dx,dy,F,C;
              for(int o=-1;o<2;o+=1)
              {
                      if(!o)dx=u+ix[f],dy=m+iy[f];
                      else dx=u,dy=m;
                      F=f+o;                    // 每次所做的事 , 还是只让时间+1 , 这样做 , bfs产生的第一个到达点就是时间最短那个。
                                        // 如果想不记录只改变方向的点 , 那么就必须用优先队列 , 时间越少优先级越高 。
                      if(!o)C=c==5?1:c+1;
                      else C=c;
                      if(F>3)F=0;
                      else if(F<0)F=3;
                      if(dx>=0&&dy>=0&&dx
                {
                    if(S[dx][dy]=='T'&&C==1)
                        return M[u][m][c][f];
                    M[dx][dy][C][F]=M[u][m][c][f]+1;
                    q1[rear]=dx;
                    q2[rear]=dy;
                    q3[rear]=C;
                    q4[rear++]=F;
                }
        }
    }
    return 0;
}
int main()
{
    int id=1;
    //freopen("1.txt","r",stdin);
    while(cin>>a>>b)
    {
        if(!a&&!b)return 0;
        else
        {
            memset(M,0,sizeof(M));
            if(id!=1)cout<<endl;
            for(int i=0;i

                      cin>>S[i];
                      int min=0;
                      for(int i=0;i
            for(int j=0;j
            {
                if(S[i][j]=='S')
                min=bfs(i,j);
            }
            cout<<"Case #"<<id++<<endl;
            if(min)
            cout<<"minimum time = "<<min<<" sec"<<endl;
            else
            cout<<"destination not reachable"<<endl;
        }
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值