迷宫 2022年国赛 BFS+记忆化搜索

思路: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();
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值