|
| 1 | +# 1765. Map of Highest Peak |
| 2 | + |
| 3 | +You are given an integer matrix `isWater` of size `m x n` that represents a map of land and water cells. |
| 4 | + |
| 5 | +* If `isWater[i][j] == 0`, cell `(i, j)` is a land cell. |
| 6 | +* If `isWater[i][j] == 1`, cell `(i, j)` is a water cell. |
| 7 | + |
| 8 | +You must assign each cell a height in a way that follows these rules: |
| 9 | + |
| 10 | +* The height of each cell must be non-negative. |
| 11 | +* If the cell is a water cell, its height must be `0`. |
| 12 | +* Any two adjacent cells must have an absolute height difference of at most `1`. |
| 13 | +* A cell is adjacent to another cell if the former is directly north, east, south, or west of the latter (i.e., their sides are touching). |
| 14 | + |
| 15 | +Find an assignment of heights such that the maximum height in the matrix is maximized. |
| 16 | + |
| 17 | +Return an integer matrix height of size `m x n` where `height[i][j]` is cell `(i, j)`'s height. |
| 18 | +If there are multiple solutions, return any of them. |
| 19 | + |
| 20 | +## 基礎思路 |
| 21 | +這可以視為一個 Multi-Source BFS 問題,由水源開始,逐漸往空白地區擴散,直到所有地區都被填滿。 |
| 22 | + |
| 23 | +> Tips 這題很容易超時,或炸記憶體,這邊引入一些小技巧 |
| 24 | +> 1. 用簡化後 Sliding Window 方向陣列,可以從 4 x 2 大小簡化到 5 x 1 (移動一次取兩位,一樣是4個方向) |
| 25 | +> 2. 用 nextQueue 來存放下一次要處理的點,減少使用 `unshift` 所帶來的效能損耗 |
| 26 | +
|
| 27 | +## 解題步驟 |
| 28 | + |
| 29 | +### Step 1: 紀錄地圖大小 |
| 30 | + |
| 31 | +```typescript |
| 32 | +const m = isWater.length; // 行數 |
| 33 | +const n = isWater[0].length; // 列數 |
| 34 | +``` |
| 35 | + |
| 36 | +### Step 2: 初始化地圖與待處理佇列 |
| 37 | + |
| 38 | +```typescript |
| 39 | +// 初始化地圖,設置所有點的高度為 -1,代表未訪問過 |
| 40 | +const heights = Array.from({ length: m }, () => Array(n).fill(-1)); |
| 41 | +let queue: [number, number, number][] = []; |
| 42 | +``` |
| 43 | + |
| 44 | +### Step 3: 初始化水源 |
| 45 | + |
| 46 | +```typescript |
| 47 | +// 初始化水源,將所有水源點的高度設為 0,並加入待處理佇列 |
| 48 | +for (let row = 0; row < m; row++) { |
| 49 | + for (let col = 0; col < n; col++) { |
| 50 | + // 如果是水源,高度設為 0,並加入待處理佇列 |
| 51 | + if (isWater[row][col] === 1) { |
| 52 | + heights[row][col] = 0; |
| 53 | + queue.push([row, col, 0]); |
| 54 | + } |
| 55 | + } |
| 56 | +} |
| 57 | +``` |
| 58 | + |
| 59 | +### Step 4: 開始 BFS |
| 60 | + |
| 61 | +```typescript |
| 62 | +while (queue.length > 0) { |
| 63 | + // 用來標記下個 BFS level 的點 |
| 64 | + const nextQueue: [number, number, number][] = []; |
| 65 | + |
| 66 | + // 遍歷當前 BFS level 的所有點 |
| 67 | + for (const [currentRow, currentCol, currentHeight] of queue) { |
| 68 | + for (let i = 0; i < 4; i++) { |
| 69 | + // 利用 Sliding Window 方向陣列,取得下一個點的位置 |
| 70 | + const nextRow = currentRow + HIGHEST_PEEK_DIRECTIONS[i]; |
| 71 | + const nextCol = currentCol + HIGHEST_PEEK_DIRECTIONS[i + 1]; |
| 72 | + |
| 73 | + // 如果下一個點超出地圖範圍,或已經訪問過,則跳過 |
| 74 | + if ( |
| 75 | + nextRow < 0 || |
| 76 | + nextRow >= m || |
| 77 | + nextCol < 0 || |
| 78 | + nextCol >= n || |
| 79 | + heights[nextRow][nextCol] !== -1 |
| 80 | + ) { |
| 81 | + continue; |
| 82 | + } |
| 83 | + |
| 84 | + // 設置旁邊的點的高度,並加入下一次處理的佇列 |
| 85 | + heights[nextRow][nextCol] = currentHeight + 1; |
| 86 | + queue.push([nextRow, nextCol, currentHeight + 1]); |
| 87 | + } |
| 88 | + } |
| 89 | + // 移動到下一個 BFS level |
| 90 | + queue = nextQueue; |
| 91 | +} |
| 92 | +``` |
| 93 | + |
| 94 | +## 時間複雜度 |
| 95 | + |
| 96 | +- 標記水源:$O(m \times n)$ |
| 97 | +- BFS 會遍歷所有點,時間複雜度為 $O(m \times n)$ |
| 98 | +- 總時間複雜度為 $O(m \times n)$ |
| 99 | + |
| 100 | +> $O(m \times n)$ |
| 101 | +
|
| 102 | +## 空間複雜度 |
| 103 | +- 額外使用了一個二維陣列 `heights` 來記錄每個點的高度,空間複雜度為 $O(m \times n)$ |
| 104 | +- `queue` 與 `nextQueue` 佇列的空間複雜度為 $O(m \times n)$ |
| 105 | +- 總空間複雜度為 $O(m \times n)$ |
| 106 | + |
| 107 | +> $O(m \times n)$ |
0 commit comments