题意:
一个R*C的海面,1<= R、C<=1000,上面每个点都有八个方向之一的current,顺着流走一个单位cost 0,不顺着走一个单位cost 1。计算由一个初始位置到结束位置最小的cost.
思路:
本来用A*,TLE,想想主要是因为A*考虑访问过的顶点,其实,这个题A*用的很憋屈,启发函数必须设成0。后来还是用了BFS,用两个队列,先把cost为0的全部入队列1,然后由队列1中的点扩展cost为1的点入队列2,直到队列1为空;再由队列2中点扩展cost为2的点,循环,直到找到目标节点。
一般的BFS求最短路径,需要一个记录顶点路径长度的表,但是,这种用两个队列的方式,就不需要这个表,而只用一个变量即可。代码中,用两个指针来操作队列就可以了,内层循环结束了swap一下两个指针。
代码:
代码
#include <cstdio>
#include <queue>
using namespace std;
struct position{
int x, y;
};
#define MAX 1000
int R, C;
char mt[MAX+1][MAX+4];
position S, T;
int move[8][2]={{-1,0}, {-1, 1}, {0,1}, {1,1},
{1,0}, {1, -1}, {0, -1}, {-1, -1}};
bool is_valid(const position & v){
return (1<=v.x && v.x<=R && 1<=v.y && v.y <=C);
}
bool visited[MAX+1][MAX+1];
int BFS(){
for(int i=1; i<=R; ++i)
for(int j=1; j<=C; ++j)
visited[i][j] = false;
int dist, cur;
position u, v;
queue<position> que1, que2;
queue<position> *popen, *pnext;
dist = 0;
popen = &que1;
pnext = &que2;
v = S;
while(is_valid(v) && !visited[v.x][v.y]){
if(v.x == T.x && v.y == T.y)
return dist;
popen->push(v);
visited[v.x][v.y] = true;
cur = mt[v.x][v.y];
v.x += move[cur][0];
v.y += move[cur][1];
}
while(true){
++dist;
while(!popen->empty()){
u = popen->front();
popen->pop();
for(int i=0; i<8; ++i){
if(i == mt[u.x][u.y])
continue;
v.x = u.x + move[i][0];
v.y = u.y + move[i][1];
while(is_valid(v) && !visited[v.x][v.y]){
if(v.x == T.x && v.y == T.y)
return dist;
pnext->push(v);
visited[v.x][v.y] = true;
cur = mt[v.x][v.y];
v.x += move[cur][0];
v.y += move[cur][1];
}
}
}
swap(popen, pnext);
}
}
int main(){
// freopen("in", "r", stdin);
while(scanf("%d %d", &R, &C) != EOF){
for(int i=1; i<=R; ++i)
scanf("%s", &mt[i][1]);
for(int i=1; i<=R; ++i)
for(int j=1; j<=C; ++j)
mt[i][j] -= '0';
int m, d;
scanf("%d", &m);
for(int i=0; i<m; ++i){
scanf("%d %d %d %d", &S.x, &S.y, &T.x, &T.y);
d = BFS();
printf("%d\n", d);
}
printf("\n");
}
return 0;
}