题目:矩阵中一共是四种点,起点,终点,路,墙。一个轮子,只能往前走,这个轮子的轮胎被分为5个部分,每个部分是一个颜色,求的是从原点走到终点,并且在终点的时候轮子着地的颜色和在起点的时候的颜色是一致的,最少多少步。
对于搜索,有的是对图搜索(树划归为图),有的是隐式图搜索。图的搜索还是比较好办,主要是对于隐式图的搜索,是比较有难度的。
最近做过的题目中,对于隐式图的搜索大多都是基于状态作为节点的,一般还有用状态压缩来记录状态(即二进制),那么位运算就是比较常用的。
那么结合这道题来说一下状态。
状态我认为就是能够唯一标识这个点的特点的集合。比如这道题,轮子在的位置,方向,和落地颜色,这几个属性决定了它的状态!这几个属性合起来可以为表示这个点。这样使得这个点不要重复遍历!
那么下面分析这道题:
首先,可以肯定的是这是一道BFS求最短路径的题目。但是不同的是,这个最短路径不是从某一个图上的点到图上另一个点的最短路径,而是从其实状态到终止状态的最短路径!
然后,我们来看一下怎么表示这个状态,显而易见的是,一定要有轮子所在的位置,它的方向,和落地的颜色。
接下来,要说一下,一个轮子在一个格子里面,可以做的操作有三个,左转,右转,前进。
最后,用一个四位的数组来记录这个状态是否被遍历过,vis[x][y][5][4],这里面有5个颜色,4个方向。
还值得注意的是,对于每一个方向,它应该有一个对应的走向,北方是向上走,以此类推,这里,我用一个数组来映射的走向,数组下标为方向的标号,{0, 1, 2, 3}一次表示北,西,南,东。
剩下的见具体的代码!
代码如下:
#include <cstdio>
#include <iostream>
#include <cstring>
#include <queue>
#include <algorithm>
using namespace std;
const int N = 30;
const int INF = 500000;
int n, m, ans;
int dis[N][N][5][4];
bool vis[N][N][5][4];
int a[4] = { -1, 0, 1, 0 }, b[4] = { 0, -1, 0, 1 };
char g[N][N];
struct Node{
int x, y, d, c;
}tmp, S, T, u;
void Bfs() {
memset( vis, 0, sizeof(vis) );
ans = INF;
queue <Node> q;
q.push(S);
vis[S.x][S.y][S.c][S.d] = true;
for ( int i = 0; i <= n; ++i )
for ( int j = 0; j <= m; ++j )
for ( int k = 0; k < 5; ++k )
for ( int g = 0; g < 4; ++g ) dis[i][j][k][g] = INF;
dis[S.x][S.y][S.c][S.d] = 0;
while ( !q.empty() ) {
u = q.front(); q.pop();
if ( u.x == T.x && u.y == T.y && u.c == T.c ) {
ans = dis[u.x][u.y][u.c][u.d];
break;
}
int d = u.d, c = u.c, x = u.x, y = u.y, cc = (c+1) % 5;
if ( g[x+a[d]][y+b[d]] != '#' && !vis[x+a[d]][y+b[d]][cc][d] && dis[x+a[d]][y+b[d]][cc][d] > dis[x][y][c][d] + 1 ) { //向前走
dis[x+a[d]][y+b[d]][cc][d] = dis[x][y][c][d] + 1;
tmp.x = x+a[d], tmp.y = y+b[d], tmp.c = cc, tmp.d = d;
vis[tmp.x][tmp.y][tmp.c][tmp.d] = 1;
q.push( tmp );
}
int d1 = (d+1)%4, d2 = d - 1;
if ( d2 < 0 ) d2 = 3;
if ( !vis[x][y][c][d1] ) { //向左转
dis[x][y][c][d1] = dis[x][y][c][d] + 1;
tmp.x = x, tmp.y = y, tmp.c = c, tmp.d = d1;
vis[tmp.x][tmp.y][tmp.c][tmp.d] = 1;
q.push( tmp );
}
if ( !vis[x][y][c][d2] ) { //向右转
dis[x][y][c][d2] = dis[x][y][c][d] + 1;
tmp.x = x, tmp.y = y, tmp.c = c, tmp.d = d2;
vis[tmp.x][tmp.y][tmp.c][tmp.d] = 1;
q.push( tmp );
}
}
}
int main()
{
int icase = 1;
while ( scanf("%d%d", &n, &m) != EOF && (m || n) ) {
getchar();
if ( icase > 1 ) cout << endl;
for ( int i = 0; i <= n+1; ++i ) g[i][0] = g[i][m+1] = '#';
for ( int j = 0; j <= m+1; ++j ) g[0][j] = g[n+1][j] = '#';
for ( int i = 1; i <= n; ++i, getchar() )
for ( int j = 1; j <= m; ++j ) {
scanf("%c", &g[i][j]);
if ( g[i][j] == 'S' ) S.x = i, S.y = j, S.d = 0, S.c = 0;
else if ( g[i][j] == 'T' ) T.x = i, T.y = j, T.c = 0;
}
//for ( int i = 0; i <= n+1; ++i ) printf("%s\n", g[i]);
Bfs();
printf("Case #%d\n", icase++);
if ( ans == INF ) printf("destination not reachable\n");
else printf("minimum time = %d sec\n", ans);
}
}