时间限制:C/C++ 1秒,其他语言2秒
空间限制:C/C++ 262144K,其他语言524288K
64bit IO Format: %lld
精通程序设计的 Applese 双写了一个游戏。
在这个游戏中,它被困在了一个
n×m的迷宫中,它想要逃出这个迷宫。
在迷宫中,有一些方格是水池,只有当 Applese 处于水属性的时候才可以通过;有一些方格是岩浆,只有当 Applese 是火属性的时候可以通过;有一些方格是墙壁,无论如何都无法通过;另一些格子是空地(包括起点和终点),可以自由通过。
在一些空地上有神秘道具可以让 Applese 转换自己的属性(从水属性变为火属性或从火属性变为水属性,需要一个单位的时间)。
已知 Applese 在一个单位的时间内可以朝四个方向行走一格,且开始处于水属性,位于空地的道具拾取后只能在该处立即使用(或者不使用),且可以多次使用。求它走出迷宫需要的最少时间。
输入描述:
第一行两个正整数 n, m 表示迷宫的大小。
接下来 n 行,每行长度为 m 的字符串。描述地图。
其中 ‘S’ 表示起点,‘T’ 表示终点,’.’ 表示空地,‘w’表示岩浆,’~‘表示水池,’@’ 表示道具,’#'表示障碍。
保证地图中的起点和终点只有一个,道具都位于空地。
输出描述:
输出一个整数,表示 Applese 走出迷宫的最短时间。特别地,如果 Applese 走不出迷宫,输出 “-1”。
示例1
输入
5 5
.w@…
.S#…
~w#…
.w…~
@w.~T
输出
18
备注
1≤n,m≤100
代码
package SearchDFSandBFS;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.LinkedList;
import java.util.PriorityQueue;
import java.util.Queue;
import java.util.StringTokenizer;
public class NewCowMazeBFS {
static int sx,sy;//起始点坐标
static int ex,ey;//终点坐标
static int m,n;//m行n列
static int[][][] vis=new int[105][105][2];
static char[][] vans=new char[105][105];
//0和~ 为1
//1和w 为1
// ~ 为 0
// w 为 1
// (0^0) ==0 (1^1)==0
// (0^1)==1 (1^1)==0
static class Node implements Comparable<Node>{
int x,y;
int ans;
int status;//0为水属性 1为火属性
Node(int x,int y,int status,int ans){
this.x=x;this.y=y;this.status=status;
this. ans=ans;
}
public int compareTo(Node o) {
return this.ans-o.ans;
}
}
static int [] dx= {-1,0,1,0};
static int [] dy= {0,1,0,-1};
static int bfs(int x,int y) {
Queue<Node>q=new PriorityQueue<Node>();
// vis[x][y][0]=1; //初始为水属性
q.add(new Node(x,y,0,0));
while(!q.isEmpty()) {
Node p=q.poll();
if(vis[p.x][p.y][p.status]==1) continue;
vis[p.x][p.y][p.status]=1;
if(p.x==ex&&p.y==ey) {
return p.ans;
}
for(int i=0;i<4;i++) {
int xx=p.x+dx[i];
int yy=p.y+dy[i];
if(xx<0||xx>=m||yy<0||yy>=n||vans[xx][yy]=='#') continue;
char ch=vans[xx][yy];
if(ch=='.'||ch=='S'||ch=='T') {
q.add(new Node(xx,yy,p.status,p.ans+1));
}else if(ch=='@') {
q.add(new Node(xx,yy,p.status,p.ans+1));
q.add(new Node(xx,yy,(p.status^1),p.ans+2));
}else {
int status =ch=='w'?1:0;
if((p.status^status)==0) {
q.add(new Node(xx,yy,p.status,p.ans+1));
}
}
}
}
return -1;
}
public static void main(String[] args) throws IOException {
BufferedReader br=new BufferedReader(new InputStreamReader(System.in));
StringTokenizer token=new StringTokenizer(br.readLine());
m=Integer.parseInt(token.nextToken());
n=Integer.parseInt(token.nextToken());
for(int i=0;i<m;i++) {
String str=br.readLine();
for(int j=0;j<n;j++) {
vis[i][j][0]=0;
vis[i][j][1]=0;
char ch=str.charAt(j);
if(ch=='S') {
sx=i;sy=j;
}
if(ch=='T') {
ex=i;ey=j;
}
vans[i][j]=ch;
}
}
System.out.println(bfs(sx,sy));
}
}
迷宫问题,求解最短路程一般使用bfs的方法,因为使用队列的问题,只要到达了终点就输出结果,结果就为最短路径的答案。
这里需要注意的是队列的选择,应该优先级队列PriorityQueue,并且使Node结点根据步数来排序,即步数最短的结点放在队列的前头,如果不实现Node的可比性并且选择的是LinkedList队列,只会通过百分之50的数据。
与经典的bfs不同的是,标记是否加入队列的数组应该设置为3维,即对不同的状态访问同一个位置是两种不同的情况。比如访问(xx,yy,~)时状态为1,应该标记vis[xx][yy][1]=1,再次达到这个点时如果状态为1,可以对这个结点的上下左右进行访问。
使用位与(异或)计算来实现状态的改变。
0代表水状态,1代表火状态。如果跳到@时,0状态变为1,1状态要变为0,所以跳到@时将这个结点的状态位与1进行异或并生成新的结点加入到队列中。
设置临时变量status,如果跳到w时status设置为1,跳到~status设置为0,将status和当前访问的结点的状态为进行异或如果为0就生成新的结点加入队列中。