主頁 >  其他 > 搜索與圖論之FloodFill

搜索與圖論之FloodFill

2021-02-06 14:15:14 其他

FloodFill

泛洪填充演算法(Flood Fill Algorithm)泛洪填充演算法又稱洪水填充演算法是在很多圖形繪制軟體中常用的填充演算法,最熟悉不過就是 windows paint的油漆桶功能,演算法的原理很簡單,就是從一個點開始附近像素點,填充成新的顏色,直到封閉區域內的所有像素點都被填充新顏色為止,泛紅填充實作最常見有四鄰域像素填充法,八鄰域像素填充法,基于掃描線的像素填充方法,根據實作又可以分為遞回與非遞回(基于堆疊),

733. 影像渲染

方法1:DFS

  • 當當前的位置的顏色和開始位置(sr,sc)的顏色不同時,不能繼續dfs
  • 如果一開始的時候,要修改的目標顏色和開始位置的顏色的值相同,不需要修改imgage
        int[][] dirs = {{-1, 0}, {0, 1}, {1, 0}, {0, -1}};
        int R, C;

        public int[][] floodFill(int[][] image, int sr, int sc, int newColor) {
            int oldColor = image[sr][sc];
            if (oldColor == newColor) return image;
            R = image.length;
            C = image[0].length;
            dfs(image, sr, sc, newColor, oldColor);
            return image;
        }
        public void dfs(int[][] image, int r, int c, int newColor, int oldColor) {
            if (!inArea(r, c)) return;
            if (image[r][c] != oldColor) return;
            image[r][c] = newColor;
            for (int[] d : dirs) {
                int nr = r + d[0], nc = c + d[1];
                dfs(image, nr, nc, newColor, oldColor);
            }
        }

        public boolean inArea(int r, int c) {
            return r >= 0 && r < R && c >= 0 && c < C;
        }

方法2:BFS

int[][] dirs = {{-1, 0}, {0, 1}, {1, 0}, {0, -1}};
int R, C;

public int[][] floodFill(int[][] image, int sr, int sc, int newColor) {
    int oldColor = image[sr][sc];
    if (oldColor == newColor) return image;
    R = image.length;
    C = image[0].length;
    Queue<int[]> queue = new LinkedList<>();
    queue.offer(new int[]{sr, sc});
    image[sr][sc] = newColor;
    while (!queue.isEmpty()) {
        int[] p = queue.poll();
        for (int[] d : dirs) {
            int nr = p[0] + d[0], nc = p[1] + d[1];
            if (!inArea(nr, nc)) continue;
            if (image[nr][nc] == oldColor) {
                image[nr][nc] = newColor;
                queue.offer(new int[]{nr, nc});
            }
        }
    }
    return image;
}

public boolean inArea(int r, int c) {
    return r >= 0 && r < R && c >= 0 && c < C;
}

1034. 邊框著色

方法1:DFS+計數

  • dfs回傳的值是0或者1
    • 當如果不是和oldColor相同的顏色,回傳1
    • 當不在區域范圍或者當前的(r,c)點被訪問過,回傳0
  • 當cnt<4時,說明有一個方向是可以突圍的,是和外面是聯通的,不是被當前點的同類顏色所包圍
        int[][] dirs = {{-1, 0}, {0, 1}, {1, 0}, {0, -1}};
        int R, C;
        boolean[][] visited;
        int oldColor;


        public int[][] colorBorder(int[][] grid, int r0, int c0, int newColor) {
            R = grid.length;
            C = grid[0].length;
            visited = new boolean[R][C];
            oldColor = grid[r0][c0];
            dfs(grid, r0, c0, newColor);
            return grid;
        }


        public int dfs(int[][] grid, int r, int c, int newColor) {
            if (!inArea(r, c)) return 0;
            if (visited[r][c]) return 1;
            if (grid[r][c] != oldColor) return 0;
            visited[r][c] = true;
            int cnt = 0;
            for (int[] d : dirs) {
                int nr = r + d[0], nc = c + d[1];
                cnt += dfs(grid, nr, nc, newColor);
            }
            if (cnt < 4) grid[r][c] = newColor;
            return 1;
        }


        public boolean inArea(int r, int c) {
            return r >= 0 && r < R && c >= 0 && c < C;
        }

方法2:DFS+翻轉成負數

思路圖片來自國際站

  • 在dfs的程序中,將其翻轉成負數,在結束的時候,翻轉負數成正數
  • 下面的思路與下圖大體相當,需要開一個額外的res二維陣列
  • res需要重新開地址賦值

在這里插入圖片描述

   int[][] dirs = {{-1, 0}, {0, 1}, {1, 0}, {0, -1}};
        int R, C;
        int[][] res;

        public int[][] colorBorder(int[][] grid, int r0, int c0, int color) {
            R = grid.length;
            C = grid[0].length;
            res = new int[R][C];
            for (int r = 0; r < R; r++)
                for (int c = 0; c < C; c++)
                    res[r][c] = grid[r][c];
            dfs(grid, r0, c0, color);
            return res;
        }


        public void dfs(int[][] grid, int r, int c, int color) {
            res[r][c] *= -1;
            boolean isEdge = false;
            boolean f = false;
            for (int[] d : dirs) {
                int nr = r + d[0], nc = c + d[1];
                if (d[0] == -1) System.out.printf("上\n");
                if (d[0] == 1) System.out.printf("下\n");
                if (d[1] == -1) System.out.printf("左\n");
                if (d[1] == 1) System.out.printf("右\n");
                //當不在區域范圍內或者當前顏色與旁邊的顏色不同時 如下面的例子1
                if (!inArea(nr, nc) || grid[r][c] != grid[nr][nc]) {
                    isEdge = true;
                }
                //res[nr][nc]首次翻轉的時候(變成負數)一定會進下面的dfs,當再次被翻轉后,其變成與grid的原坐標相同的時候,不會再進
                if (inArea(nr, nc) && res[nr][nc] == grid[r][c]) {
                    dfs(grid, nr, nc, color);
                }

            }
            res[r][c] *= -1;
            if (isEdge) res[r][c] = color;
        }

        public boolean inArea(int r, int c) {
            return r >= 0 && r < R && c >= 0 && c < C;
        }

例1

當在(0,3)位置,掃其右側的(0,4)位置時,滿足grid[r][c] != grid[nr][nc]

[[1,2,1,2,1,2],[2,2,2,2,1,2],[1,2,2,2,1,2]]
1
3
1

方法3:BFS

int[][] dirs = {{-1, 0}, {0, 1}, {1, 0}, {0, -1}};
int R, C;

public int[][] colorBorder(int[][] grid, int r0, int c0, int color) {
    R = grid.length;
    C = grid[0].length;
    int oldColor = grid[r0][c0];
    boolean[][] visited = new boolean[R][C];
    Queue<int[]> queue = new LinkedList<>();
    queue.offer(new int[]{r0, c0});
    visited[r0][c0] = true;
    while (!queue.isEmpty()) {
        int[] p = queue.poll();
        int r = p[0], c = p[1];
        if (isBorder(r, c)) grid[r][c] = color;
        for (int[] d : dirs) {
            if (d[0] == -1) System.out.printf("上\n");
            if (d[0] == 1) System.out.printf("下\n");
            if (d[1] == -1) System.out.printf("左\n");
            if (d[1] == 1) System.out.printf("右\n");
            int nr = r + d[0], nc = c + d[1];
            System.out.printf("(%d,%d)<--->(%d,%d)\n", r, c, nr, nc);
            if (!inArea(nr, nc) || visited[nr][nc]) continue;
            if (grid[nr][nc] == oldColor) {//挨著的點不是原來的oldColor,當前點便是異類
                queue.offer(new int[]{nr, nc});
                visited[nr][nc] = true;
            } else {
                grid[r][c] = color;
            }
        }
    }
    return grid;
}

//注意是或
public boolean isBorder(int r, int c) {
    return r == 0 || r == R - 1 || c == 0 || c == C - 1;
}

public boolean inArea(int r, int c) {
    return r >= 0 && r < R && c >= 0 && c < C;
}

配的例子

int[][] grid = new int[][]{
        {1, 1, 1, 1, 2, 2},
        {3, 3, 3, 3, 2, 2},
        {3, 3, 3, 2, 3, 3},
        {3, 3, 2, 3, 3, 3},
        {3, 2, 2, 2, 3, 3},
        {3, 2, 2, 2, 3, 3},
        {3, 3, 2, 2, 3, 3}
};
int color = 4;
int r0 = 5, c0 = 1;

785. 判斷二分圖

這個問題可以抽象為:給定一個無向圖,判斷是否能找到一個使用兩種顏色的著色方案,使每條邊連接的兩點顏色均不同

方法1:DFS染色

public boolean isBipartite(int[][] graph) {
    int[] visited = new int[graph.length];
    for (int i = 0; i < graph.length; i++) {
        //當前點沒有被訪問過且染色失敗,回傳false
        if (visited[i] == 0 && !dfs(graph, i, 1, visited)) return false;
    }
    return true;
}


/**
 * @param graph   圖
 * @param curr    當前處理的頂點
 * @param color   當前頂點即將被染的顏色
 * @param visited 記錄頂點是否被訪問過
 * @return 成功染色,回傳true,失敗染色回傳false
 */
public boolean dfs(int[][] graph, int curr, int color, int[] visited) {
    if (visited[curr] != 0) {
        return visited[curr] == color;
    }
    visited[curr] = color;
    for (int next : graph[curr]) {
        if (!dfs(graph, next, -color, visited)) return false;
    }
    return true;
}

方法2:BFS染色

public boolean isBipartite(int[][] graph) {
    int[] v = new int[graph.length];
    for (int i = 0; i < graph.length; i++) {
        //對沒有染色并且有鄰居的進行下面的回圈
        if (v[i] == 0 && graph[i].length > 0) {
            Queue<Integer> q = new LinkedList<>();
            q.offer(i);
            v[i] = 1;
            while (!q.isEmpty()) {
                int size = q.size();
                for (int j = 0; j < size; j++) {
                    int curr = q.poll();
                    for (int next : graph[curr]) {
                        //如果curr的鄰居處于沒有被染色的狀態,染上一與curr相反的顏色,curr為1,next為-1,curr為-1,next為1
                        if (v[next] == 0) {
                            q.offer(next);
                            v[next] = -1 * v[curr];
                        } 
                        //這時候next已經染上色了,開始對其染色進行判斷,如果next與curr同色,不符合題意
                        else if (v[curr] == v[next]) {
                            return false;
                        }
                    }
                }
            }
        }
    }
    return true;
}

886. 可能的二分法

  • 需要構建圖,其他的模板類似于785二分圖這題

方法1:DFS染色

  • 構造圖,其余的和785一毛一樣
public boolean possibleBipartition(int N, int[][] dislikes) {
    int[] visited = new int[N + 1];
    List<Integer>[] graph = new List[N + 1];
    for (int i = 1; i <= N; ++i) graph[i] = new ArrayList<>();
    for (int[] d : dislikes) {
        graph[d[0]].add(d[1]);
        graph[d[1]].add(d[0]);
    }
    for (int i = 1; i <= N; ++i) {
        //當前點沒有被訪問過且染色失敗,回傳false
        if (visited[i] == 0 && !dfs(graph, i, 1, visited)) return false;
    }
    return true;
}

/**
 * @param graph   圖
 * @param curr    當前處理的頂點
 * @param color   當前頂點即將被染的顏色
 * @param visited 記錄頂點是否被訪問過
 * @return 成功染色,回傳true,失敗染色回傳false
 */
public boolean dfs(List<Integer>[] graph, int curr, int color, int[] visited) {
    if (visited[curr] != 0) {
        return visited[curr] == color;
    }
    visited[curr] = color;
    for (int next : graph[curr]) {
        if (!dfs(graph, next, -color, visited)) return false;
    }
    return true;
}

方法2:BFS染色

public boolean possibleBipartition(int N, int[][] dislikes) {
    //v 表示從1 到 N 的節點,其各自的顏色,初始化為0, 兩種顏色 1 和-1
    int[] v = new int[N + 1];
    //構造圖
    List<Integer>[] graph = new List[N + 1];
    for (int i = 1; i <= N; ++i) graph[i] = new ArrayList<>();
    //無向圖
    for (int[] d : dislikes) {
        graph[d[0]].add(d[1]);
        graph[d[1]].add(d[0]);
    }
    for (int i = 1; i <= N; ++i) {
        if (v[i] != 0) continue;//已經染色的不需要再染色了
        Queue<Integer> q = new LinkedList<>();
        q.offer(i);
        v[i] = 1;//染色1
        while (!q.isEmpty()) {
            int curr = q.poll();
            for (int next : graph[curr]) {
                if (v[next] != 0) {//next如果是和curr一樣的顏色,說明是同類,但是他們各自不喜歡,回傳false
                    if (v[curr] == v[next]) return false;
                } else {
                    q.offer(next);
                    v[next] = -v[curr];//翻轉next的顏色為curr的相反數
                }
            }
        }
    }
    return true;
}

番外

構建圖的另外的方式

Map<Integer, Set<Integer>> graph = new HashMap<>();
for (int[] d : dislikes) {
    int s = d[0], t = d[1];
    graph.putIfAbsent(s, new HashSet<>());
    graph.putIfAbsent(t, new HashSet<>());
    graph.get(s).add(t);
    graph.get(t).add(s);
}

529. 掃雷游戲

方法1:DFS

  • 八方向,需要額外多做一個探測地雷的操作detect
int R, C;
int[][] dirs = new int[][]{{-1, -1}, {-1, 0}, {-1, 1}, {0, 1}, {1, 1}, {1, 0}, {1, -1}, {0, -1}};

public char[][] updateBoard(char[][] board, int[] click) {
    R = board.length;
    C = board[0].length;
    dfs(board, click[0], click[1]);
    return board;
}

public void dfs(char[][] board, int r, int c) {
    if (!inArea(r, c)) return;
    if (board[r][c] == 'M') {
        board[r][c] = 'X';
        return;
    }
    if (board[r][c] == 'E') {
        detect(board, r, c);
        if (board[r][c] == 'B') {
            for (int[] d : dirs) {
                int nr = r + d[0], nc = c + d[1];
                dfs(board, nr, nc);
            }
        }
    }
}

public void detect(char[][] board, int r, int c) {
    int bomb = 0;
    for (int[] d : dirs) {
        int nr = r + d[0], nc = c + d[1];
        if (inArea(nr, nc) && board[nr][nc] == 'M') bomb++;
    }
    board[r][c] = bomb > 0 ? (char) (bomb + '0') : 'B';
}


private boolean inArea(int r, int c) {
    return r >= 0 && r < R && c >= 0 && c < C;
}

方法2:BFS

  • visited控制重復訪問
int R, C;
int[][] dirs = new int[][]{{-1, -1}, {-1, 0}, {-1, 1}, {0, 1}, {1, 1}, {1, 0}, {1, -1}, {0, -1}};

public char[][] updateBoard(char[][] board, int[] click) {
    R = board.length;
    C = board[0].length;
    int sr = click[0], sc = click[1];
    if (board[sr][sc] == 'M') {
        board[sr][sc] = 'X';
        return board;
    }
    bfs(board,sr,sc);
    return board;
}


public void bfs(char[][] board, int r, int c) {
    boolean[][] visited = new boolean[R][C];
    Queue<int[]> q = new LinkedList<>();
    q.offer(new int[]{r, c});
    visited[r][c] = true;
    while (!q.isEmpty()) {
        int[] curr = q.poll();
        int currR = curr[0], currC = curr[1];
        detect(board, currR, currC);
        if (board[currR][currC] == 'B') {
            for (int[] d : dirs) {
                int nextR = currR + d[0], nextC = currC + d[1];
                if (inArea(nextR, nextC) && !visited[nextR][nextC]) {
                    q.offer(new int[]{nextR, nextC});
                    visited[nextR][nextC] = true;
                }
            }
        }
    }
}


private boolean inArea(int r, int c) {
    return r >= 0 && r < R && c >= 0 && c < C;
}


public void detect(char[][] board, int r, int c) {
    int bomb = 0;
    for (int[] d : dirs) {
        int nr = r + d[0], nc = c + d[1];
        if (inArea(nr, nc) && board[nr][nc] == 'M') bomb++;
    }
    board[r][c] = bomb > 0 ? (char) (bomb + '0') : 'B';
}

200. 島嶼數量

島嶼問題之島嶼的數量Eighty-eight Butterfly

827. 最大人工島

島嶼問題之最大人工島Danaus Genutia

695. 島嶼的最大面積

島嶼問題之島嶼的周長面積Morpho Cypris Aphrodite

463. 島嶼的周長

島嶼問題之島嶼的周長面積Morpho Cypris Aphrodite

1254. 統計封閉島嶼的數目

島嶼問題之不同島嶼的數量Monarch Butterfly

130. 被圍繞的區域

島嶼問題之被圍繞的區域[Cicada]

289. 生命游戲

方法1:BFS染色

  int R, C;
        int[][] dirs = new int[][]{{-1, -1}, {-1, 0}, {-1, 1}, {0, 1}, {1, 1}, {1, 0}, {1, -1}, {0, -1}};


        public void gameOfLife(int[][] board) {
            if (board == null || (board.length == 0 || board[0].length == 0)) return;
            R = board.length;
            C = board[0].length;
            bfs(board, 0, 0);
//            PrintUtils.printMatrix(board);
            for (int r = 0; r < R; ++r)
                for (int c = 0; c < C; ++c)
                    //被標記的發生翻轉
                    if (board[r][c] == -1) board[r][c] = 0;
                    else if (board[r][c] == -2) board[r][c] = 1;
//            PrintUtils.printMatrix(board);
        }


        public void bfs(int[][] board, int sr, int sc) {
            boolean[][] visited = new boolean[R][C];
            Queue<int[]> q = new LinkedList<>();
            q.offer(new int[]{sr, sc});
            visited[sr][sc] = true;//標記原始(sr,sc)被訪問過
            while (!q.isEmpty()) {
                int[] curr = q.poll();
                int cr = curr[0], cc = curr[1];//當前點
                int cnt = 0;
                for (int[] d : dirs) {
                    int nr = cr + d[0], nc = cc + d[1];
                    if (!inArea(nr, nc)) continue;
                   	//當前為1 或者我們暫時標記的-1 cnt++
                    if (board[nr][nc] == 1 || board[nr][nc] == -1) cnt++;
                    if (visited[nr][nc]) continue;
                    q.offer(new int[]{nr, nc});
                    visited[nr][nc] = true;
                }
//                System.out.printf("(%d,%d)-%d\n", cr, cc, cnt);
                if (board[cr][cc] == 1) {//當前細胞為活細胞
                    if (cnt < 2 || cnt > 3) board[cr][cc] = -1;//<2 >3 兩種情況下需要設定當前的活細胞為死細胞,區別0這種,我們設定為-1
                    else if (cnt == 2 || cnt == 3) board[cr][cc] = 1;//等于2 等于3 活細胞繼續活著
                } else if (board[cr][cc] == 0) {//當前細胞為死細胞
                    if (cnt == 3) board[cr][cc] = -2;//死細胞復活,區別1這種活細胞,設定為-2
                }
            }
        }


        private boolean inArea(int r, int c) {
            return r >= 0 && r < R && c >= 0 && c < C;
        }

方法2:位操作

來自國際站@yavinci大神,地址在這里

  • 可以使用2 bits來存盤這幾種狀態
[2nd bit, 1st bit] = [next state, current state]
//從左到右是第2位,第1位, 
- 00  dead (next) <- dead (current)  0(10) //十進制的0
- 01  dead (next) <- live (current)  1(10) //十進制的1
- 10  live (next) <- dead (current)  2(10) //十進制的2
- 11  live (next) <- live (current)  3(10) //十進制的3
  • 一些解釋

    • 一開始的時候,只有0和1 也就是只有0001這兩種二進制狀態
    • 注意到第1位的狀態1st與第2位2nd的狀態是獨立的
    • 想象下,所有的細胞從1st狀態切換到2nd狀態
    • 計算當前點從1st狀態的存活著的鄰居,依據規則設定2nd狀態
    • 因為2nd默認是0,也就是死亡的,沒有必要考慮01->00
    • 在結束前,移除1st狀態,保留2nd的狀態,回傳board 執行>> 操作即可
  • 檢查每個細胞的 1st狀態,然后根據存活狀態的鄰居數量來設定2nd

01->11 //一開始的時候是存活的,繼續保持存活需要滿足:
    board[r][c]== 1 && lives in (2,3)
00->10 //一開始的時候是死亡的,轉成存活需要滿足:
    board[r][c]==0 && lives in (3)

位操作小技巧

//獲取當前低位 1st
board[[r][c]&1 
//獲取下個狀態位 即高位 2nd
board[r][c]>>1
  • 與( & )每一位進行比較,兩位都為1,結果為1,否則為0(-4 & 1 = 0)
  1 0 0 1 1 -->(19)[10]  表示10進制中的19
& 1 1 0 0 1 -->(25)[10]
------------------------------
  1 0 0 0 1 -->(17)[10]
  • 右移( >> ) 整體右移,左邊空出位補零或補1(負數補1,整數補0),右邊位舍棄 (-4 >> 1 = -2)
unsigned int a = 8;
a >> 3;
移位前:0000 0000 0000 0000 0000 0000 0000 1000 -->(8)[10]
移位后:0000 0000 0000 0000 0000 0000 0000 0001 -->(1)[10] 相當于 / 2^3

int a = -8;
a >> 3;
移位前:1111 1111 1111 1111 1111 1111 1111 1000 -->(-8)[10]
移位前:1111 1111 1111 1111 1111 1111 1111 1111 -->(-1)[10] 
int R, C;

public void gameOfLife(int[][] board) {
    if (board == null || board.length == 0) return;
    R = board.length;
    C = board[0].length;
    PrintUtils.printMatrix(board, true);
    for (int r = 0; r < R; r++) {
        for (int c = 0; c < C; c++) {
            int lives = getLiveNeighbors1(board, r, c);
            //一開始的時候,所有的2nd都是0,因此只需要確定2nd是否變成1
            // 01 --> 11 做2nd位 3想成是十進制的11
            if (board[r][c] == 1 && (lives >= 2 && lives <= 3)) board[r][c] = 3;
            //00  --> 10 做2nd位 2想成是十進制的10
            if (board[r][c] == 0 && lives == 3) board[r][c] = 2;
        }
    }
    PrintUtils.printMatrix(board, true);
    for (int r = 0; r < R; r++) {
        for (int c = 0; c < C; c++) {
            board[r][c] >>= 1;
        }
    }

}


public int getLiveNeighbors(int[][] board, int sr, int sc) {
    int lives = 0;
    //遍歷的時候拿的是sr,sc一圈為1的9個點,有1的+1
    for (int r = Math.max(sr - 1, 0); r <= Math.min(sr + 1, R - 1); r++) {
        for (int c = Math.max(sc - 1, 0); c <= Math.min(sc + 1, C - 1); c++) {
            lives += board[r][c] & 1;
        }
    }
    //去掉自己是1的情況
    lives -= board[sr][sc] & 1;
    return lives;
}

另外一種求lives的方式:

        public int getLiveNeighbors1(int[][] board, int sr, int sc) {
            int[][] dirs = new int[][]{{-1, -1}, {-1, 0}, {-1, 1}, {0, 1}, {1, 1}, {1, 0}, {1, -1}, {0, -1}};
            int lives = 0;
            for (int[] d : dirs) {
                int nr = sr + d[0], nc = sc + d[1];
                if (!inArea(nr, nc)) continue;
                lives += board[nr][nc] & 1;
            }
            return lives;
        }

        private boolean inArea(int r, int c) {
            return r >= 0 && r < R && c >= 0 && c < C;
        }

列印出位操作前后的二進制的狀態

//原始的board
     0      1      0 
     0      0      1 
     1      1      1 
     0      0      0 
----
//原始的board 二進制
00 01 00 
00 00 01 
01 01 01 
00 00 00 
--------------
//位操作后的board 二進制
00 01 00 
10 00 11 
01 11 11 
00 10 00 
//取2nd 位的結果即可回傳

1020. 飛地的數量

類似130題 被圍繞的區域

方法1:BFS染色

int[][] dirs = {{-1, 0}, {0, 1}, {1, 0}, {0, -1}};
int R, C;

public int numEnclaves(int[][] A) {
    R = A.length;
    C = A[0].length;
    PrintUtils.printMatrix(A);
    for (int r = 0; r < R; r++) {
        for (int c = 0; c < C; c++) {
            if (isBoard(r, c) && A[r][c] == 1) {//邊界上的1作為種子來進行擴散
                bfs(A, r, c);
            }
        }
    }
    //統計1的個數(即封閉的陸地的個數)
    int cnt = 0;
    for (int r = 0; r < R; r++) {
        for (int c = 0; c < C; c++) {
            if (A[r][c] == 1) cnt++;
        }
    }
    return cnt;
}


private void bfs(int[][] A, int sr, int sc) {
    Queue<int[]> q = new LinkedList<>();
    q.offer(new int[]{sr, sc});
    A[sr][sc] = 0;//邊緣進來的點是1,染色為0
    while (!q.isEmpty()) {
        int[] c = q.poll();
        int cr = c[0], cc = c[1];
        for (int[] d : dirs) {
            int nr = cr + d[0], nc = cc + d[1];
            if (!inArea(nr, nc)) continue;
            if (A[nr][nc] == 1) {//如果當前點是陸地1,翻轉為0
                q.offer(new int[]{nr, nc});
                A[nr][nc] = 0;
            }
        }
    }
}

//邊緣的點
private boolean isBoard(int r, int c) {
    return r == 0 || r == R - 1 || c == 0 || c == C - 1;
}

//在區域內的點
private boolean inArea(int r, int c) {
    return r >= 0 && r < R && c >= 0 && c < C;
}

方法2:DFS染色

int[][] dirs = {{-1, 0}, {0, 1}, {1, 0}, {0, -1}};
int R, C;

public int numEnclaves(int[][] A) {
    R = A.length;
    C = A[0].length;
    for (int r = 0; r < R; r++) {
        for (int c = 0; c < C; c++) {
            if (isBoard(r, c) && A[r][c] == 1) {
                dfs(A, r, c);
            }
        }
    }
    //統計1的個數(即封閉的陸地的個數)
    int cnt = 0;
    for (int r = 0; r < R; r++) {
        for (int c = 0; c < C; c++) {
            if (A[r][c] == 1) cnt++;
        }
    }
    return cnt;

}


private void dfs(int[][] A, int r, int c) {
    if (!inArea(r, c) || A[r][c] != 1) return;
    A[r][c] = 0;
    for (int[] d : dirs) {
        dfs(A, r + d[0], c + d[1]);
    }
}

//邊緣的點
private boolean isBoard(int r, int c) {
    return r == 0 || r == R - 1 || c == 0 || c == C - 1;
}

//在區域內的點
private boolean inArea(int r, int c) {
    return r >= 0 && r < R && c >= 0 && c < C;
}

轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/257189.html

標籤:其他

上一篇:NEFU 2021大一寒假集訓總結賽 全題解

下一篇:控制工程中的數學建模(2)——二階有源低通濾波器(之二)

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 網閘典型架構簡述

    網閘架構一般分為兩種:三主機的三系統架構網閘和雙主機的2+1架構網閘。 三主機架構分別為內端機、外端機和仲裁機。三機無論從軟體和硬體上均各自獨立。首先從硬體上來看,三機都用各自獨立的主板、記憶體及存盤設備。從軟體上來看,三機有各自獨立的作業系統。這樣能達到完全的三機獨立。對于“2+1”系統,“2”分為 ......

    uj5u.com 2020-09-10 02:00:44 more
  • 如何從xshell上傳檔案到centos linux虛擬機里

    如何從xshell上傳檔案到centos linux虛擬機里及:虛擬機CentOs下執行 yum -y install lrzsz命令,出現錯誤:鏡像無法找到軟體包 前言 一、安裝lrzsz步驟 二、上傳檔案 三、遇到的問題及解決方案 總結 前言 提示:其實很簡單,往虛擬機上安裝一個上傳檔案的工具 ......

    uj5u.com 2020-09-10 02:00:47 more
  • 一、SQLMAP入門

    一、SQLMAP入門 1、判斷是否存在注入 sqlmap.py -u 網址/id=1 id=1不可缺少。當注入點后面的引數大于兩個時。需要加雙引號, sqlmap.py -u "網址/id=1&uid=1" 2、判斷文本中的請求是否存在注入 從文本中加載http請求,SQLMAP可以從一個文本檔案中 ......

    uj5u.com 2020-09-10 02:00:50 more
  • Metasploit 簡單使用教程

    metasploit 簡單使用教程 浩先生, 2020-08-28 16:18:25 分類專欄: kail 網路安全 linux 文章標簽: linux資訊安全 編輯 著作權 metasploit 使用教程 前言 一、Metasploit是什么? 二、準備作業 三、具體步驟 前言 Msfconsole ......

    uj5u.com 2020-09-10 02:00:53 more
  • 游戲逆向之驅動層與用戶層通訊

    驅動層代碼: #pragma once #include <ntifs.h> #define add_code CTL_CODE(FILE_DEVICE_UNKNOWN,0x800,METHOD_BUFFERED,FILE_ANY_ACCESS) /* 更多游戲逆向視頻www.yxfzedu.com ......

    uj5u.com 2020-09-10 02:00:56 more
  • 北斗電力時鐘(北斗授時服務器)讓網路資料更精準

    北斗電力時鐘(北斗授時服務器)讓網路資料更精準 北斗電力時鐘(北斗授時服務器)讓網路資料更精準 京準電子科技官微——ahjzsz 近幾年,資訊技術的得了快速發展,互聯網在逐漸普及,其在人們生活和生產中都得到了廣泛應用,并且取得了不錯的應用效果。計算機網路資訊在電力系統中的應用,一方面使電力系統的運行 ......

    uj5u.com 2020-09-10 02:01:03 more
  • 【CTF】CTFHub 技能樹 彩蛋 writeup

    ?碎碎念 CTFHub:https://www.ctfhub.com/ 筆者入門CTF時時剛開始刷的是bugku的舊平臺,后來才有了CTFHub。 感覺不論是網頁UI設計,還是題目質量,賽事跟蹤,工具軟體都做得很不錯。 而且因為獨到的金幣制度的確讓人有一種想去刷題賺金幣的感覺。 個人還是非常喜歡這個 ......

    uj5u.com 2020-09-10 02:04:05 more
  • 02windows基礎操作

    我學到了一下幾點 Windows系統目錄結構與滲透的作用 常見Windows的服務詳解 Windows埠詳解 常用的Windows注冊表詳解 hacker DOS命令詳解(net user / type /md /rd/ dir /cd /net use copy、批處理 等) 利用dos命令制作 ......

    uj5u.com 2020-09-10 02:04:18 more
  • 03.Linux基礎操作

    我學到了以下幾點 01Linux系統介紹02系統安裝,密碼啊破解03Linux常用命令04LAMP 01LINUX windows: win03 8 12 16 19 配置不繁瑣 Linux:redhat,centos(紅帽社區版),Ubuntu server,suse unix:金融機構,證券,銀 ......

    uj5u.com 2020-09-10 02:04:30 more
  • 05HTML

    01HTML介紹 02頭部標簽講解03基礎標簽講解04表單標簽講解 HTML前段語言 js1.了解代碼2.根據代碼 懂得挖掘漏洞 (POST注入/XSS漏洞上傳)3.黑帽seo 白帽seo 客戶網站被黑帽植入劫持代碼如何處理4.熟悉html表單 <html><head><title>TDK標題,描述 ......

    uj5u.com 2020-09-10 02:04:36 more
最新发布
  • 2023年最新微信小程式抓包教程

    01 開門見山 隔一個月發一篇文章,不過分。 首先回顧一下《微信系結手機號資料庫被脫庫事件》,我也是第一時間得知了這個訊息,然后跟蹤了整件事情的經過。下面是這起事件的相關截圖以及近日流出的一萬條資料樣本: 個人認為這件事也沒什么,還不如關注一下之前45億快遞資料查詢渠道疑似在近日復活的訊息。 訊息是 ......

    uj5u.com 2023-04-20 08:48:24 more
  • web3 產品介紹:metamask 錢包 使用最多的瀏覽器插件錢包

    Metamask錢包是一種基于區塊鏈技術的數字貨幣錢包,它允許用戶在安全、便捷的環境下管理自己的加密資產。Metamask錢包是以太坊生態系統中最流行的錢包之一,它具有易于使用、安全性高和功能強大等優點。 本文將詳細介紹Metamask錢包的功能和使用方法。 一、 Metamask錢包的功能 數字資 ......

    uj5u.com 2023-04-20 08:47:46 more
  • vulnhub_Earth

    前言 靶機地址->>>vulnhub_Earth 攻擊機ip:192.168.20.121 靶機ip:192.168.20.122 參考文章 https://www.cnblogs.com/Jing-X/archive/2022/04/03/16097695.html https://www.cnb ......

    uj5u.com 2023-04-20 07:46:20 more
  • 從4k到42k,軟體測驗工程師的漲薪史,給我看哭了

    清明節一過,盲猜大家已經無心上班,在數著日子準備過五一,但一想到銀行卡里的余額……瞬間心情就不美麗了。最近,2023年高校畢業生就業調查顯示,本科畢業月平均起薪為5825元。調查一出,便有很多同學表示自己又被平均了。看著這一資料,不免讓人想到前不久中國青年報的一項調查:近六成大學生認為畢業10年內會 ......

    uj5u.com 2023-04-20 07:44:00 more
  • 最新版本 Stable Diffusion 開源 AI 繪畫工具之中文自動提詞篇

    🎈 標簽生成器 由于輸入正向提示詞 prompt 和反向提示詞 negative prompt 都是使用英文,所以對學習母語的我們非常不友好 使用網址:https://tinygeeker.github.io/p/ai-prompt-generator 這個網址是為了讓大家在使用 AI 繪畫的時候 ......

    uj5u.com 2023-04-20 07:43:36 more
  • 漫談前端自動化測驗演進之路及測驗工具分析

    隨著前端技術的不斷發展和應用程式的日益復雜,前端自動化測驗也在不斷演進。隨著 Web 應用程式變得越來越復雜,自動化測驗的需求也越來越高。如今,自動化測驗已經成為 Web 應用程式開發程序中不可或缺的一部分,它們可以幫助開發人員更快地發現和修復錯誤,提高應用程式的性能和可靠性。 ......

    uj5u.com 2023-04-20 07:43:16 more
  • CANN開發實踐:4個DVPP記憶體問題的典型案例解讀

    摘要:由于DVPP媒體資料處理功能對存放輸入、輸出資料的記憶體有更高的要求(例如,記憶體首地址128位元組對齊),因此需呼叫專用的記憶體申請介面,那么本期就分享幾個關于DVPP記憶體問題的典型案例,并給出原因分析及解決方法。 本文分享自華為云社區《FAQ_DVPP記憶體問題案例》,作者:昇騰CANN。 DVPP ......

    uj5u.com 2023-04-20 07:43:03 more
  • msf學習

    msf學習 以kali自帶的msf為例 一、msf核心模塊與功能 msf模塊都放在/usr/share/metasploit-framework/modules目錄下 1、auxiliary 輔助模塊,輔助滲透(埠掃描、登錄密碼爆破、漏洞驗證等) 2、encoders 編碼器模塊,主要包含各種編碼 ......

    uj5u.com 2023-04-20 07:42:59 more
  • Halcon軟體安裝與界面簡介

    1. 下載Halcon17版本到到本地 2. 雙擊安裝包后 3. 步驟如下 1.2 Halcon軟體安裝 界面分為四大塊 1. Halcon的五個助手 1) 影像采集助手:與相機連接,設定相機引數,采集影像 2) 標定助手:九點標定或是其它的標定,生成標定檔案及內參外參,可以將像素單位轉換為長度單位 ......

    uj5u.com 2023-04-20 07:42:17 more
  • 在MacOS下使用Unity3D開發游戲

    第一次發博客,先發一下我的游戲開發環境吧。 去年2月份買了一臺MacBookPro2021 M1pro(以下簡稱mbp),這一年來一直在用mbp開發游戲。我大致分享一下我的開發工具以及使用體驗。 1、Unity 官網鏈接: https://unity.cn/releases 我一般使用的Apple ......

    uj5u.com 2023-04-20 07:40:19 more