|
| 1 | +# 2017. Grid Game |
| 2 | + |
| 3 | +You are given a 0-indexed 2D array `grid` of size `2 x n`, |
| 4 | +where `grid[r][c]` represents the number of points at position `(r, c)` on the matrix. |
| 5 | +Two robots are playing a game on this matrix. |
| 6 | + |
| 7 | +Both robots initially start at `(0, 0)` and want to reach `(1, n-1)`. |
| 8 | +Each robot may only move to the right `((r, c)` to `(r, c + 1))` or |
| 9 | +down `((r, c)` to `(r + 1, c))`. |
| 10 | + |
| 11 | +At the start of the game, the first robot moves from `(0, 0)` to `(1, n-1)`, |
| 12 | +collecting all the points from the cells on its path. |
| 13 | +For all cells `(r, c)` traversed on the path, `grid[r][c]` is set to 0. |
| 14 | +Then, the second robot moves from `(0, 0)` to `(1, n-1)`, |
| 15 | +collecting the points on its path. |
| 16 | +Note that their paths may intersect with one another. |
| 17 | + |
| 18 | +The first robot wants to minimize the number of points collected by the second robot. |
| 19 | +In contrast, the second robot wants to maximize the number of points it collects. |
| 20 | +If both robots play optimally, return the number of points collected by the second robot. |
| 21 | + |
| 22 | +## 基礎思路 |
| 23 | + |
| 24 | +我們可以將問題轉換為分析 `grid` 的兩個獨立一維陣列:`grid[0]` 和 `grid[1]`。 |
| 25 | +考慮 **第一機器人**的行動邏輯: |
| 26 | + |
| 27 | +1. **第一機器人轉移點**: |
| 28 | + 當第一機器人在第 `index` 位置從 `grid[0]` 切換到 `grid[1]` 時: |
| 29 | + - `grid[0]`:第 `index` 位置之前(包含 `index`)的所有分數會被清空。 |
| 30 | + - `grid[1]`:第 `index` 位置之後(包含 `index`)的所有分數會被清空。 |
| 31 | + |
| 32 | +2. **剩餘的有效分數**: |
| 33 | + 經過上述清空後: |
| 34 | + - `grid[0]` 有效分數範圍為第 `index + 1` 之後的部分。 |
| 35 | + - `grid[1]` 有效分數範圍為第 `index - 1` 之前的部分。 |
| 36 | + |
| 37 | +**機器人二**的目標是最大化分數,根據剩餘分數,其得分可能來自以下兩種情況: |
| 38 | +1. **選擇剩餘的 `grid[0]` 分數**:吃完 `grid[0]` 的所有分數。 |
| 39 | +2. **選擇剩餘的 `grid[1]` 分數**:吃完 `grid[1]` 的所有分數。 |
| 40 | + |
| 41 | +由於機器人二會選擇最大化得分的路徑,因此其最終得分為上述兩者中的較大值。 |
| 42 | + |
| 43 | +我們的目標是檢索所有可能的 `index`,對每個 `index` 計算機器人二的得分,並在所有情況中選擇 **最小化機器人二得分** 的情況。最小值即為答案。 |
| 44 | + |
| 45 | +> 小技巧: |
| 46 | +> - 一個節省空間的方法是先紀錄 topSum 是整個 `grid[0]` 加總。並先把 bottomSum 的設為 `0`。 |
| 47 | +> - 當是 index 時,此時的上方的分數是 topSum 減去 `grid[0][index]`,下方的分數是 bottomSum 加上 `grid[1][index]`。 |
| 48 | +> - 這樣能大幅減少計算量與所需暫存空間。 |
| 49 | +
|
| 50 | +## 解題步驟 |
| 51 | + |
| 52 | +### Step 1: 紀錄 N 長度 |
| 53 | + |
| 54 | +```typescript |
| 55 | +const n = grid[0].length; |
| 56 | +``` |
| 57 | + |
| 58 | +### Step 2: 初始化 `topSum` 以及 `bottomSum` |
| 59 | + |
| 60 | +```typescript |
| 61 | +let topSum = grid[0].reduce((a, b) => a + b, 0); // 紀錄 topSum 是整個 `grid[0]` 加總 |
| 62 | +let bottomSum = 0; // 初始化 bottomSum為 `0` |
| 63 | +``` |
| 64 | + |
| 65 | +### Step 3: 計算每個 `index` 的得分並記錄最小值 |
| 66 | + |
| 67 | +```typescript |
| 68 | +// 初始化最小值 |
| 69 | +let minSecondRobotScore = Infinity; |
| 70 | + |
| 71 | +// 模擬每個 index 的情況 |
| 72 | +for (let i = 0; i < n; i++) { |
| 73 | + // 先減去 當前 index 在 grid[0] 的分數 |
| 74 | + // 因為對於 topSum 來說,需要的是 index 之後的累積分數 |
| 75 | + topSum -= grid[0][i]; |
| 76 | + |
| 77 | + // 計算第二機器人再該 index 分割下的能獲取的最大分數 |
| 78 | + const secondRobotScore = Math.max(topSum, bottomSum); |
| 79 | + |
| 80 | + // 更新第二機器人可能獲取的最小分數 |
| 81 | + minSecondRobotScore = Math.min(minSecondRobotScore, secondRobotScore); |
| 82 | + |
| 83 | + // 移動到下一個 index 須把當前 index 的分數加入 bottomSum |
| 84 | + // 因為 bottomSum 是從計算 index 之前的累積分數 |
| 85 | + bottomSum += grid[1][i]; |
| 86 | +} |
| 87 | +``` |
| 88 | + |
| 89 | +## 時間複雜度 |
| 90 | +- 計算加總需要 $O(n)$ 時間 |
| 91 | +- 計算每個 index 的得分需要 $O(n)$ 時間 |
| 92 | +- 因此總時間複雜度為 $O(n)$。 |
| 93 | + |
| 94 | +> $O(n)$ |
| 95 | +
|
| 96 | +## 空間複雜度 |
| 97 | +- 我們只使用了常數額外空間,因此空間複雜度為 $O(1)$。 |
| 98 | +- 這是一個非常高效的解決方案。 |
| 99 | + |
| 100 | +> $O(1)$ |
0 commit comments