思路:BFS+记忆化搜索
首先求的是每个点到终点的最短路径,如果每个点都来一遍BFS那就太多重复计算了。所以这里可以想到记忆化搜索,即一个dp[i] = min(dp[i+1],dp[i-1],dp[i+N],dp[i-N],dp[i])。min函数中的dp[i]代表通过传送门计算的距离,也就是一个点的最短路径由他上下左右和传送门后的点的最小值确定。
平常的记忆化搜索都是min(dp[i+1],dp[i+N]),也就是他左移和下移一格位置的最小值。但是因为本题传送门的问题,导致可能上面的点可以更快的到达终点。
对于遍历,我们利用BFS的思想,以广度优先搜索第一遍历的肯定要比后来遍历到该点路径要短。我们逆着从终点开始遍历,从终点开始左上遍历,遍历时不要忘记传送门。
AC代码:
import java.io.*;
import java.util.*;
public class Main{
static int N ;
private static HashMap<Integer, ArrayList<Integer>> map;
public static void main(String[] args) throws IOException {
StreamTokenizer in = new StreamTokenizer(new BufferedReader(new InputStreamReader(System.in)));
map = new HashMap<>();
in.nextToken();
N = (int) in.nval;
in.nextToken();
int M = (int) in.nval;
int t1,t2,t3,t4,t,tt;
for (int i = 0; i < M; i++) {
in.nextToken();t1 = (int) in.nval;
in.nextToken();t2 = (int) in.nval;
in.nextToken();t3 = (int) in.nval;
in.nextToken();t4 = (int) in.nval;
t = (t1-1)*N + t2;
tt = (t3-1)*N + t4;
if(map.get(t)==null){
ArrayList<Integer> l = new ArrayList<>();
l.add(tt);
map.put(t,l);
}else {
map.get(t).add(tt);
}
if(map.get(tt)==null){
ArrayList<Integer> l = new ArrayList<>();
l.add(t);
map.put(tt,l);
}else {
map.get(tt).add(t);
}
}
System.out.printf("%.2f%n",bfs(N*N)*1.0/(N*N));
}
static int bfs(int start){
int count = 0; //已找到最短路径的个数
LinkedList<Integer> result = new LinkedList<>();
Queue<Integer> queue = new LinkedList<>(); // 队列存储待访问节点
boolean[] visited = new boolean[N*N+1]; // 用set存储已访问节点
int[] dp = new int[N*N+1];
Arrays.fill(dp, 0x7ffffff);
queue.add(start);
visited[start] = true;
int temp,t1,t2,t3,t4,sum=0;
while(!queue.isEmpty()){
temp = queue.poll();
//开始计算dp[temp] 去上下左右和自己本身中的最小值
if(temp>N*(N-1)) t1 = temp; else t1 = temp+N;
if(temp%N==0) t2 = temp; else t2 = temp + 1;
if((temp-1)%N==0) t3 = temp; else t3 = temp - 1;
if(temp<=N) t4 = temp; else t4 = temp - N;
dp[temp] = Math.min(Math.min(Math.min(dp[t3],dp[t4])+1,Math.min(dp[t1],dp[t2]) + 1),dp[temp]+1);
if(temp == N*N) dp[temp] = 0;
count++;
sum+=dp[temp];
if(count==N*N){
//所有节点的最短路径都已经找到
return sum;
}
//放入
result.add(temp);
if(temp%N!=0&&!visited[temp+1]) {
//这个位置是迷宫的最右端格子 不能再往右走了
queue.add(temp+1);
visited[temp+1] = true;
}
if((temp-1)%N!=0&&!visited[temp-1]){
//这个位置是迷宫的最左端格子 不能再往左走了
queue.add(temp-1);
visited[temp-1] = true;
}
if(temp>N&&!visited[temp-N]){
queue.add(temp-N);
visited[temp-N] = true;
}
if(temp<N*(N-1)-1&&!visited[temp+N]){
queue.add(temp+N);
visited[temp+N] = true;
}
ArrayList<Integer> chuan = map.get(temp);
//传送门
if(chuan!=null){
for (Integer integer : chuan) {
if(!visited[integer]){
queue.add(integer);
//将传送门对面的格子的dp值从无穷大设为dp[temp] 后面比较的时候再加一
dp[integer] = dp[temp];
visited[integer] = true;
}
}
}
}
return result.size();
}
}