题目:Instrusive
题意:给定一个地图,要从'M'点到'T'点,每次可以往四个方向移动,平时每次移动1格花费1秒。但是由于地图上有一些监控,如果当前所在格被监控看到,就必须躲在纸箱里,躲在纸箱里移动一格的耗时是3秒。而监控的可视范围是它本身所在的一格,以及它朝向的相邻一格。监控每秒会顺时针旋转90度。地图上还有一些‘#'标记表示不可以进入的。
最后求M'到'T'的最短时间,不能到达输出-1。
因为每个监控都是1秒旋转90度,换句话说,所有监控的朝向都是可以通过时间来判断的,而当前时间T监控的朝向,跟它在T%4的时候是一样的。所以可以将地图按照模4分成4层,在预处理的时候就标记好对应模4的时刻哪些点是被监控到的。
再来是移动,对于当前结点,由于进出纸箱是不耗时的。所以如果是在本地停留1秒的话,不管有没有被监控,都是转移到这个点的下一秒。
如果打算移动了,就看看当前格和下一格是否在当前时刻会被监控,如果两个都没有被监控,移动花费是1,否则是3。
确定了这些之后就是写个bfs了,标记好哪些状态已经被访问过就行了。
#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;
const int N = 510;
char map[N][N];
bool ban[N][N][4], vis[N][N][4];
struct Node{
int x, y, u;
Node(){}
Node(int x, int y, int u):x(x),y(y),u(u){}
bool operator < (const Node &A)const{
return u > A.u;
}
};
int xl[4]={-1,0,1,0};
int yl[4]={0,1,0,-1};
int getd(char ch){
if(ch=='N') return 0;
if(ch=='E') return 1;
if(ch=='S') return 2;
if(ch=='W') return 3;
return -1;
}
int T, n, sx, sy;
int solve(){
memset(vis, 0, sizeof(vis));
priority_queue<Node> Q;
Q.push(Node(sx, sy, 0));
while(!Q.empty()){
Node nd=Q.top(); Q.pop();
if(vis[nd.x][nd.y][nd.u%4]) continue;
if(map[nd.x][nd.y]=='T') return nd.u;
vis[nd.x][nd.y][nd.u%4]=1;
Q.push(Node(nd.x, nd.y, nd.u+1));
for(int i=0; i<4; i++){
int a = nd.x + xl[i];
int b = nd.y + yl[i];
if(a<0 || a>=n || b<0 || b>=n) continue;
if(map[a][b]=='#') continue;
int u = nd.u;
if(ban[a][b][nd.u%4] || ban[nd.x][nd.y][nd.u%4]){
u += 3;
}
else{
u++;
}
if(!vis[a][b][u%4]) Q.push(Node(a,b,u));
}
}
return -1;
}
int main(){
scanf("%d",&T);
for(int t=1; t<=T; t++){
scanf("%d", &n);
memset(ban, 0, sizeof(ban));
for(int i=0; i<n; i++){
scanf("%s", map[i]);
for(int j=0; j<n; j++){
if(map[i][j]=='M'){
sx=i; sy=j;
}
else{
int d = getd(map[i][j]);
if(~d){
for(int k=0; k<4; k++){
ban[i][j][k] = 1;
int x = i + xl[(d+k)%4];
int y = j + yl[(d+k)%4];
if(x<0 || x>=n || y<0 || y>=n) continue;
ban[x][y][k] = 1;
}
}
}
}
}
printf("Case #%d: %d\n", t, solve());
}
return 0;
}