这是一道复合状态的迷宫搜索题 。
对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;
}
对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
{
};
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()
{
}
//判断状态是否出现过
bool is_ok( int x , int y , int cc , int d )
{
}
//得到步数
int get_step( int nd , int d , int s )
{
}
//判断是否为解
bool is_ans( int x , int y , int cc )
{
}
//广度优先搜索
void bfs()
{
}
int main()
{
}
第二种情况的代码:
#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 main()
{
}