题目:矩阵中一共是四种点,起点,终点,路,墙。一个轮子,只能往前走,这个轮子的轮胎被分为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); } }