1. 程式人生 > 其它 >BFS(二)轉動轉盤鎖

BFS(二)轉動轉盤鎖

對應 LeetCode 752.轉動轉盤鎖


### 問題定義

你有一個帶有四個圓形撥輪的轉盤鎖。每個撥輪都有10個數字: '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' 。每個撥輪可以自由旋轉:例如把 '9' 變為 '0','0' 變為 '9' 。每次旋轉都只能旋轉一個撥輪的一位數字。

鎖的初始數字為 '0000' ,一個代表四個撥輪的數字的字串

列表 deads 包含了一組死亡數字,一旦撥輪的數字和列表裡的任何一個元素相同,這個鎖將會被永久鎖定,無法再被旋轉。

字串 target 代表可以解鎖的數字,你需要給出解鎖需要的最小旋轉次數,如果無論如何不能解鎖,返回 -1 。

解決思路

  • 窮舉

    這個問題比較簡單,使用窮舉的方式列出從 "0000" 開始滿足所有條件的轉動情況,進行轉動分析即可

    這裡的窮舉使用 BFS 是一個很好的思路,每層的高度就對應著轉動的次數,只要當前層中存在目標數字 \(target\) ,那麼當前的層數就是其搜尋的次數

  • 雙向 BFS 優化

    由於每層的每個數字的都可以向上或向下轉動,因此在搜尋過程中將會出現 “搜尋爆炸” 的情況。可選的解決方案交替使用從上和從下的搜尋方式進行遍歷,這樣就能夠有效地解決 “搜尋爆炸” 的問題

細節處理:實際上,有些被搜尋過的數字可能在之後會再次出現,因此需要記錄之前已經搜尋過的數字;使用雙向搜尋的方式進行搜尋時,使用 Map

來記錄兩個方向的搜尋次數,當搜尋成功時相加即可。

實現

  • 一般的窮舉

    class Solution {
        Set<String> deadSet = new HashSet<>();
        static String start = "0000";
        Set<String> accessed = new HashSet<>();
    
        public int openLock(String[] deadends, String target) {
            for (String s : deadends) deadSet.add(s);
            
            // 特殊情況處理
            if (deadSet.contains(start) || deadSet.contains(target)) 
                return -1;
            if (target.equals(start)) return 0;
    
            Deque<String> deque = new LinkedList<>();
            deque.offer(start);
            accessed.add(start);
    
            int ans = 0;
            while (!deque.isEmpty()) {
                int size = deque.size();
                ans++;
    
                while (size-- > 0) {
                    String word = deque.poll();
                    for (int i = 0; i < 4; ++i) {
                        String plus = plus(word.toCharArray(), i);
                        if (!deadSet.contains(plus) && !accessed.contains(plus)) {
                            if (plus.equals(target)) return ans;
                            deque.offer(plus);
                            accessed.add(plus);
                        }
    
                        String minus = minus(word.toCharArray(), i);
                        if (!deadSet.contains(minus) && !accessed.contains(minus)) {
                            if (minus.equals(target)) return ans;
                            deque.offer(minus);
                            accessed.add(minus);
                        }
                    }
                }
            }
    
            return -1;
        }
        
        // 指定的數字位 +1
        String plus(char[] array, int index) {
            if (array[index] < '9') array[index] = (char) (array[index] + 1);
            else array[index] = '0';
    
            return String.valueOf(array);
        }
        
        // 指定的數字位 -1
        String minus(char[] array, int index) {
            if (array[index] > '0') array[index] = (char) (array[index] - 1);
            else array[index] = '9';
    
            return String.valueOf(array);
        }
    }
    

    複雜度分析:略

  • 雙向 BFS

    class Solution {
        Set<String> deadSet = new HashSet<>();
        static String start = "0000";
        Set<String> accessed = new HashSet<>();
    
        public int openLock(String[] deadends, String target) {
            for (String s : deadends) deadSet.add(s);
    
            if (deadSet.contains(start) || deadSet.contains(target)) 
                return -1;
            if (target.equals(start)) return 0;
    
            Deque<String> top = new LinkedList<>();
            Deque<String> bottom = new LinkedList<>();
            Map<String, Integer> topMap = new HashMap<>();
            Map<String, Integer> bottomMap = new HashMap<>();
    
            top.offer(start);
            topMap.put(start, 0);
    
            bottom.offer(target);
            bottomMap.put(target, 0);
    
            while (!top.isEmpty() && !bottom.isEmpty()) {
                int t = -1;
                if (top.size() <= bottom.size()) {
                    t = update(top, bottom, topMap, bottomMap);
                } else {
                    t = update(bottom, top, bottomMap, topMap);
                }
    
                if (t != -1) return t;
            }
    
            return -1;
        }
    
        int update(
            Deque<String> d1,
            Deque<String> d2,
            Map<String, Integer> map1,
            Map<String, Integer> map2
        ) {
            int size = d1.size();
    
            while (size-- > 0) {
                String word = d1.poll();
    
                for (int i = 0; i < 4; ++i) {
                    String plus = plus(word.toCharArray(), i);
                    if (!deadSet.contains(plus) && !map1.containsKey(plus)) {
                        if (map2.containsKey(plus)) 
                            return map1.get(word) + map2.get(plus) + 1; // 本次已經再轉動了一次
                        d1.offer(plus);
                        map1.put(plus, map1.get(word) + 1);
                    }
    
                    String minus = minus(word.toCharArray(), i);
                    if (!deadSet.contains(minus) && !map1.containsKey(minus)) {
                        if (map2.containsKey(minus)) 
                            return map1.get(word) + map2.get(minus) + 1;
                        d1.offer(minus);
                        map1.put(minus, map1.get(word) + 1);
                    }
                }
            }
    
            return -1;
        }
    
        String plus(char[] array, int index) {
            if (array[index] < '9') array[index] = (char) (array[index] + 1);
            else array[index] = '0';
    
            return String.valueOf(array);
        }
    
        String minus(char[] array, int index) {
            if (array[index] > '0') array[index] = (char) (array[index] - 1);
            else array[index] = '9';
    
            return String.valueOf(array);
        }
    }
    

    複雜度分析:略