来源第 97 场双周赛 Q4难度中等分数2368
题目描述
给你一个下标从 0 开始的 m x n 二进制 矩阵 grid 。你可以从一个格子 (row, col) 移动到格子 (row + 1, col) 或者 (row, col + 1) ,前提是前往的格子值为 1 。如果从 (0, 0) 到 (m - 1, n - 1) 没有任何路径,我们称该矩阵是 不连通 的。
你可以翻转 最多一个 格子的值(也可以不翻转)。你 不能翻转 格子 (0, 0) 和 (m - 1, n - 1) 。
如果可以使矩阵不连通,请你返回 true ,否则返回 false 。
注意 ,翻转一个格子的值,可以使它的值从 0 变 1 ,或从 1 变 0 。
示例 1:

输入:grid = [[1,1,1],[1,0,0],[1,1,1]]
输出:true
解释:按照上图所示我们翻转蓝色格子里的值,翻转后从 (0, 0) 到 (2, 2) 没有路径。
示例 2:

输入:grid = [[1,1,1],[1,0,1],[1,1,1]]
输出:false
解释:无法翻转至多一个格子,使 (0, 0) 到 (2, 2) 没有路径。
提示:
m == grid.length n == grid[i].length 1 <= m, n <= 1000 1 <= m * n <= 105 grid[0][0] == grid[m - 1][n - 1] == 1
解法
方法一:两次 DFS
思考
只能向右或向下走,问能否翻转至多一个非端点格子,使左上到右下不连通。枚举翻转位置再判连通是立方级。
网格上若存在两条顶点内部不交的路径,则必须翻两格才能切断。先 DFS 找一条路径并把途经格(除恢复的两端)置 \(0\),再 DFS 一次:若仍能到达,说明存在第二条不相交路径,无法用一次翻转断开。
我们先进行一次 DFS,判断从 \((0, 0)\) 到 \((m - 1, n - 1)\) 是否存在路径,记结果为 \(a\)。在 DFS 的过程中,我们将访问过的格子的值置为 \(0\),以防止重复访问。
接下来,我们将 \((0, 0)\) 和 \((m - 1, n - 1)\) 的值置为 \(1\),再进行一次 DFS,判断从 \((0, 0)\) 到 \((m - 1, n - 1)\) 是否存在路径,记结果为 \(b\)。在 DFS 的过程中,我们将访问过的格子的值置为 \(0\),避免重复访问。
最后,如果 \(a\) 和 \(b\) 都为 true,则返回 false,否则返回 true。
时间复杂度 \(O(mn)\),空间复杂度 \(O(mn)\)。其中 \(m\) 和 \(n\) 分别是矩阵的行数和列数。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 | class Solution:
def isPossibleToCutPath(self, grid: List[List[int]]) -> bool:
def dfs(i, j):
if i >= m or j >= n or grid[i][j] == 0:
return False
grid[i][j] = 0
if i == m - 1 and j == n - 1:
return True
return dfs(i + 1, j) or dfs(i, j + 1)
m, n = len(grid), len(grid[0])
a = dfs(0, 0)
grid[0][0] = grid[-1][-1] = 1
b = dfs(0, 0)
return not (a and b)
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27 | class Solution {
private int[][] grid;
private int m;
private int n;
public boolean isPossibleToCutPath(int[][] grid) {
this.grid = grid;
m = grid.length;
n = grid[0].length;
boolean a = dfs(0, 0);
grid[0][0] = 1;
grid[m - 1][n - 1] = 1;
boolean b = dfs(0, 0);
return !(a && b);
}
private boolean dfs(int i, int j) {
if (i >= m || j >= n || grid[i][j] == 0) {
return false;
}
if (i == m - 1 && j == n - 1) {
return true;
}
grid[i][j] = 0;
return dfs(i + 1, j) || dfs(i, j + 1);
}
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 | class Solution {
public:
bool isPossibleToCutPath(vector<vector<int>>& grid) {
int m = grid.size(), n = grid[0].size();
function<bool(int, int)> dfs = [&](int i, int j) -> bool {
if (i >= m || j >= n || grid[i][j] == 0) {
return false;
}
if (i == m - 1 && j == n - 1) {
return true;
}
grid[i][j] = 0;
return dfs(i + 1, j) || dfs(i, j + 1);
};
bool a = dfs(0, 0);
grid[0][0] = grid[m - 1][n - 1] = 1;
bool b = dfs(0, 0);
return !(a && b);
}
};
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 | func isPossibleToCutPath(grid [][]int) bool {
m, n := len(grid), len(grid[0])
var dfs func(i, j int) bool
dfs = func(i, j int) bool {
if i >= m || j >= n || grid[i][j] == 0 {
return false
}
if i == m-1 && j == n-1 {
return true
}
grid[i][j] = 0
return dfs(i+1, j) || dfs(i, j+1)
}
a := dfs(0, 0)
grid[0][0], grid[m-1][n-1] = 1, 1
b := dfs(0, 0)
return !(a && b)
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 | function isPossibleToCutPath(grid: number[][]): boolean {
const m = grid.length;
const n = grid[0].length;
const dfs = (i: number, j: number): boolean => {
if (i >= m || j >= n || grid[i][j] !== 1) {
return false;
}
grid[i][j] = 0;
if (i === m - 1 && j === n - 1) {
return true;
}
return dfs(i + 1, j) || dfs(i, j + 1);
};
const a = dfs(0, 0);
grid[0][0] = 1;
grid[m - 1][n - 1] = 1;
const b = dfs(0, 0);
return !(a && b);
}
|
方法二:两次显式栈
思考
只能向右或向下走,问能否翻转至多一个非端点格子,使左上到右下不连通。枚举翻转位置再判连通是立方级。网格上若存在两条内部不相交的路径,一次翻转就不够。先向下优先找出一条路径,把经过的格子清零,两端再恢复,然后同样再搜一次。行数可以到 \(1000\),而第一步总是向下,调用深度就是 \(m\)。因此用显式栈保持同一顺序:先压入右方,再压入下方。
搜索使用显式栈,进入格子时把它置为 \(0\),先处理下方再处理右方。
我们先用显式栈搜索一次,判断从 \((0, 0)\) 到 \((m - 1, n - 1)\) 是否存在路径,记结果为 \(a\)。搜索过程中,我们将访问过的格子的值置为 \(0\),以防止重复访问。
接下来,我们将 \((0, 0)\) 和 \((m - 1, n - 1)\) 的值置为 \(1\),再用显式栈搜索一次,判断从 \((0, 0)\) 到 \((m - 1, n - 1)\) 是否存在路径,记结果为 \(b\)。搜索过程中,我们将访问过的格子的值置为 \(0\),避免重复访问。
最后,如果 \(a\) 和 \(b\) 都为 true,则返回 false,否则返回 true。
时间复杂度 \(O(mn)\),空间复杂度 \(O(mn)\)。其中 \(m\) 和 \(n\) 分别是矩阵的行数和列数。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 | class Solution:
def isPossibleToCutPath(self, grid: List[List[int]]) -> bool:
def dfs() -> bool:
stk = [(0, 0)]
while stk:
i, j = stk.pop()
if i >= m or j >= n or grid[i][j] == 0:
continue
grid[i][j] = 0
if i == m - 1 and j == n - 1:
return True
stk.append((i, j + 1))
stk.append((i + 1, j))
return False
m, n = len(grid), len(grid[0])
a = dfs()
grid[0][0] = grid[-1][-1] = 1
b = dfs()
return not (a and b)
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36 | class Solution {
public boolean isPossibleToCutPath(int[][] grid) {
int m = grid.length;
int n = grid[0].length;
boolean a = dfs(grid, m, n);
grid[0][0] = 1;
grid[m - 1][n - 1] = 1;
boolean b = dfs(grid, m, n);
return !(a && b);
}
private boolean dfs(int[][] grid, int m, int n) {
int[] stk = new int[m * n * 2];
int top = 0;
stk[top++] = 0;
while (top > 0) {
int cur = stk[--top];
int i = cur / n;
int j = cur % n;
if (i >= m || j >= n || grid[i][j] == 0) {
continue;
}
grid[i][j] = 0;
if (i == m - 1 && j == n - 1) {
return true;
}
if (j + 1 < n) {
stk[top++] = i * n + j + 1;
}
if (i + 1 < m) {
stk[top++] = (i + 1) * n + j;
}
}
return false;
}
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27 | class Solution {
public:
bool isPossibleToCutPath(vector<vector<int>>& grid) {
int m = grid.size(), n = grid[0].size();
auto dfs = [&]() {
vector<pair<int, int>> stk{{0, 0}};
while (!stk.empty()) {
auto [i, j] = stk.back();
stk.pop_back();
if (i >= m || j >= n || grid[i][j] == 0) {
continue;
}
grid[i][j] = 0;
if (i == m - 1 && j == n - 1) {
return true;
}
stk.emplace_back(i, j + 1);
stk.emplace_back(i + 1, j);
}
return false;
};
bool a = dfs();
grid[0][0] = grid[m - 1][n - 1] = 1;
bool b = dfs();
return !(a && b);
}
};
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23 | func isPossibleToCutPath(grid [][]int) bool {
m, n := len(grid), len(grid[0])
dfs := func() bool {
stk := [][2]int{{0, 0}}
for len(stk) > 0 {
i, j := stk[len(stk)-1][0], stk[len(stk)-1][1]
stk = stk[:len(stk)-1]
if i >= m || j >= n || grid[i][j] == 0 {
continue
}
grid[i][j] = 0
if i == m-1 && j == n-1 {
return true
}
stk = append(stk, [2]int{i, j + 1}, [2]int{i + 1, j})
}
return false
}
a := dfs()
grid[0][0], grid[m-1][n-1] = 1, 1
b := dfs()
return !(a && b)
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24 | function isPossibleToCutPath(grid: number[][]): boolean {
const m = grid.length;
const n = grid[0].length;
const dfs = (): boolean => {
const stk: number[][] = [[0, 0]];
while (stk.length) {
const [i, j] = stk.pop()!;
if (i >= m || j >= n || grid[i][j] !== 1) {
continue;
}
grid[i][j] = 0;
if (i === m - 1 && j === n - 1) {
return true;
}
stk.push([i, j + 1], [i + 1, j]);
}
return false;
};
const a = dfs();
grid[0][0] = 1;
grid[m - 1][n - 1] = 1;
const b = dfs();
return !(a && b);
}
|