【Lintcode】1708. Shortest Bridge

題目地址:

https://www.lintcode.com/problem/shortest-bridge/description

給定一個二維010-1矩陣,含11的連通塊形成一個島嶼。題目保證矩陣中有兩個島嶼,允許將其中的00變爲11,問至少改變多少個00能使得兩個島嶼連通。

思路是雙向BFS。首先需要將兩個島嶼區分開來,所以先遍歷矩陣,將其中一個含11的連通塊標記爲22,這一步可以用DFS來做。接着將標記爲11的島嶼和標記爲22的島嶼分別進兩個隊列做雙向BFS,每次擴展一步邊界,即將邊界向外走一格的00改變爲島嶼的編號11或者22,直到走到交界處爲止,返回走的步數即可。代碼如下:

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;

public class Solution {
    public int ShortestBridge(int[][] A) {
        Queue<int[]> beginQueue = new LinkedList<>(), endQueue = new LinkedList<>();
        
        // marked記錄有沒有找到島嶼
        boolean marked = false;
        for (int i = 0; i < A.length; i++) {
            for (int j = 0; j < A[0].length; j++) {
            	// 一旦找到了島嶼,就將其標記爲2,然後記marked爲true
                if (A[i][j] == 1) {
                    dfs(i, j, A);
                    marked = true;
                    break;
                }
            }
            // 如果標記過其中一個島嶼了,就退出循環
            if (marked) {
                break;
            }
        }
    
    	// 將標記爲1和標記爲2的兩個島嶼分別加進兩個隊列
        for (int i = 0; i < A.length; i++) {
            for (int j = 0; j < A[0].length; j++) {
                if (A[i][j] == 1) {
                    beginQueue.offer(new int[]{i, j});
                } else if (A[i][j] == 2) {
                    endQueue.offer(new int[]{i, j});
                }
            }
        }
        
        int res = 0;
        while (!beginQueue.isEmpty() && !endQueue.isEmpty()) {
            int beginSize = beginQueue.size(), endSize = endQueue.size();
            for (int i = 0; i < beginSize; i++) {
                int[] cur = beginQueue.poll();
                // 得到cur走下一步的不等於1的點,如果發現了2則返回步數,否則標記爲1並加入隊列
                for (int[] next : getNexts(cur[0], cur[1], A, 1)) {
                    if (A[next[0]][next[1]] == 2) {
                        return res;
                    }
                    A[next[0]][next[1]] = 1;
                    beginQueue.offer(next);
                }
            }
            
            res++;
            for (int i = 0; i < endSize; i++) {
                int[] cur = endQueue.poll();
                for (int[] next : getNexts(cur[0], cur[1], A, 2)) {
                    if (A[next[0]][next[1]] == 1) {
                        return res;
                    }
                    A[next[0]][next[1]] = 2;
                    endQueue.offer(next);
                }
            }
            res++;
        }
        
        // 這個問題的解一定是存在的,所以這一步事實上是走不到的
        return 0;
    }
    
    // 返回A[x][y]四個方向走一步的所有不等於mark的點
    private List<int[]> getNexts(int x, int y, int[][] A, int mark) {
        List<int[]> nexts = new ArrayList<>();
        int[] d = {0, 1, 0, -1, 0};
        for (int i = 0; i < 4; i++) {
            int nextX = x + d[i], nextY = y + d[i + 1];
            if (inBound(nextX, nextY, A) && A[nextX][nextY] != mark) {
                nexts.add(new int[]{nextX, nextY});
            }
        }
        
        return nexts;
    }
    
    private void dfs(int x, int y, int[][] A) {
        A[x][y] = 2;
        int[] d = {0, 1, 0, -1, 0};
        for (int i = 0; i < 4; i++) {
            int nextX = x + d[i], nextY = y + d[i + 1];
            if (inBound(nextX, nextY, A) && A[nextX][nextY] == 1) {
                dfs(nextX, nextY, A);
            }
        }
    }
    
    private boolean inBound(int x, int y, int[][] A) {
        return 0 <= x && x < A.length && 0 <= y && y < A[0].length;
    }
}

時空複雜度O(mn)O(mn)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章