Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit ccd0110

Browse files
Merge pull request SharingSource#601 from SharingSource/ac_oier
✨feat: add 749
2 parents 3ddabc8 + a2694a0 commit ccd0110

File tree

3 files changed

+225
-0
lines changed

3 files changed

+225
-0
lines changed

‎Index/图论 BFS.md‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
| [417. 太平洋大西洋水流问题](https://leetcode-cn.com/problems/pacific-atlantic-water-flow/) | [LeetCode 题解链接](https://leetcode-cn.com/problems/pacific-atlantic-water-flow/solution/by-ac_oier-do7d/) | 中等 | 🤩🤩🤩🤩🤩 |
66
| [433. 最小基因变化](https://leetcode-cn.com/problems/minimum-genetic-mutation/) | [LeetCode 题解链接](https://leetcode-cn.com/problems/minimum-genetic-mutation/solution/by-ac_oier-74b4/) | 中等 | 🤩🤩🤩🤩🤩 |
77
| [675. 为高尔夫比赛砍树](https://leetcode.cn/problems/cut-off-trees-for-golf-event/) | [LeetCode 题解链接](https://leetcode.cn/problems/cut-off-trees-for-golf-event/solution/by-ac_oier-ksth/) | 困难 | 🤩🤩🤩🤩🤩 |
8+
| [749. 隔离病毒](https://leetcode.cn/problems/contain-virus/) | [LeetCode 题解链接](https://leetcode.cn/problems/contain-virus/solution/by-ac_oier-l9ya/) | 困难 | 🤩🤩🤩 |
89
| [752. 打开转盘锁](https://leetcode-cn.com/problems/open-the-lock/) | [LeetCode 题解链接](https://leetcode-cn.com/problems/open-the-lock/solution/gong-shui-san-xie-yi-ti-shuang-jie-shuan-wyr9/) | 中等 | 🤩🤩🤩🤩 |
910
| [773. 滑动谜题](https://leetcode-cn.com/problems/sliding-puzzle/) | [LeetCode 题解链接](https://leetcode-cn.com/problems/sliding-puzzle/solution/gong-shui-san-xie-fa-hui-a-suan-fa-zui-d-3go8/) | 困难 | 🤩🤩🤩🤩 |
1011
| [815. 公交路线](https://leetcode-cn.com/problems/bus-routes/) | [LeetCode 题解链接](https://leetcode-cn.com/problems/bus-routes/solution/gong-shui-san-xie-yi-ti-shuang-jie-po-su-1roh/) | 困难 | 🤩🤩🤩🤩 |

‎Index/模拟.md‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@
5454
| [434. 字符串中的单词数](https://leetcode-cn.com/problems/number-of-segments-in-a-string/) | [LeetCode 题解链接](https://leetcode-cn.com/problems/number-of-segments-in-a-string/solution/gong-shui-san-xie-jian-dan-zi-fu-mo-ni-t-0gx6/) | 简单 | 🤩🤩🤩🤩 |
5555
| [440. 字典序的第K小数字](https://leetcode-cn.com/problems/k-th-smallest-in-lexicographical-order/) | [LeetCode 题解链接](https://leetcode-cn.com/problems/k-th-smallest-in-lexicographical-order/solution/by-ac_oier-m3zl/) | 困难 | 🤩🤩 |
5656
| [443. 压缩字符串](https://leetcode-cn.com/problems/string-compression/) | [LeetCode 题解链接](https://leetcode-cn.com/problems/string-compression/solution/gong-shui-san-xie-shuang-zhi-zhen-yuan-d-bppu/) | 中等 | 🤩🤩🤩🤩 |
57+
| [749. 隔离病毒](https://leetcode.cn/problems/contain-virus/) | [LeetCode 题解链接](https://leetcode.cn/problems/contain-virus/solution/by-ac_oier-l9ya/) | 困难 | 🤩🤩🤩 |
5758
| [451. 根据字符出现频率排序](https://leetcode-cn.com/problems/sort-characters-by-frequency/) | [LeetCode 题解链接](https://leetcode-cn.com/problems/sort-characters-by-frequency/solution/gong-shui-san-xie-shu-ju-jie-gou-yun-yon-gst9/) | 中等 | 🤩🤩🤩🤩 |
5859
| [457. 环形数组是否存在循环](https://leetcode-cn.com/problems/circular-array-loop/) | [LeetCode 题解链接](https://leetcode-cn.com/problems/circular-array-loop/solution/gong-shui-san-xie-yi-ti-shuang-jie-mo-ni-ag05/) | 中等 | 🤩🤩🤩🤩 |
5960
| [468. 验证IP地址](https://leetcode.cn/problems/validate-ip-address/) | [LeetCode 题解链接](https://leetcode.cn/problems/validate-ip-address/solution/by-ac_oier-s217/) | 中等 | 🤩🤩🤩🤩🤩 |
Lines changed: 223 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,223 @@
1+
### 题目描述
2+
3+
这是 LeetCode 上的 **[749. 隔离病毒](https://leetcode.cn/problems/contain-virus/solution/by-ac_oier-l9ya/)** ,难度为 **困难**
4+
5+
Tag : 「模拟」、「图论搜索」、「BFS」
6+
7+
8+
9+
病毒扩散得很快,现在你的任务是尽可能地通过安装防火墙来隔离病毒。
10+
11+
假设世界由 $m \times n$ 的二维矩阵 `isInfected` 组成,`isInfected[i][j] == 0` 表示该区域未感染病毒,而 `isInfected[i][j] == 1` 表示该区域已感染病毒。可以在任意 2ドル$ 个相邻单元之间的共享边界上安装一个防火墙(并且只有一个防火墙)。
12+
13+
每天晚上,病毒会从被感染区域向相邻未感染区域扩散,除非被防火墙隔离。现由于资源有限,每天你只能安装一系列防火墙来隔离其中一个被病毒感染的区域(一个区域或连续的一片区域),且该感染区域对未感染区域的威胁最大且 保证唯一 。
14+
15+
你需要努力使得最后有部分区域不被病毒感染,如果可以成功,那么返回需要使用的防火墙个数; 如果无法实现,则返回在世界被病毒全部感染时已安装的防火墙个数。
16+
17+
示例 1:
18+
![](https://assets.leetcode.com/uploads/2021/06/01/virus11-grid.jpg)
19+
```
20+
输入: isInfected = [[0,1,0,0,0,0,0,1],[0,1,0,0,0,0,0,1],[0,0,0,0,0,0,0,1],[0,0,0,0,0,0,0,0]]
21+
22+
输出: 10
23+
24+
解释:一共有两块被病毒感染的区域。
25+
在第一天,添加 5 墙隔离病毒区域的左侧。病毒传播后的状态是:
26+
27+
第二天,在右侧添加 5 个墙来隔离病毒区域。此时病毒已经被完全控制住了。
28+
```
29+
![](https://assets.leetcode.com/uploads/2021/06/01/virus13edited-grid.jpg)
30+
示例 2:
31+
![](https://assets.leetcode.com/uploads/2021/06/01/virus2-grid.jpg)
32+
```
33+
输入: isInfected = [[1,1,1],[1,0,1],[1,1,1]]
34+
35+
输出: 4
36+
37+
解释: 虽然只保存了一个小区域,但却有四面墙。
38+
注意,防火墙只建立在两个不同区域的共享边界上。
39+
```
40+
示例 3:
41+
```
42+
输入: isInfected = [[1,1,1,0,0,0,0,0,0],[1,0,1,0,1,1,1,1,1],[1,1,1,0,0,0,0,0,0]]
43+
44+
输出: 13
45+
46+
解释: 在隔离右边感染区域后,隔离左边病毒区域只需要 2 个防火墙。
47+
```
48+
49+
提示:
50+
* $m == isInfected.length$
51+
* $n == isInfected[i].length$
52+
* 1ドル <= m, n <= 50$
53+
* `isInfected[i][j]` is either `0` or `1`
54+
* 在整个描述的过程中,总有一个相邻的病毒区域,它将在下一轮 严格地感染更多未受污染的方块
55+
56+
---
57+
58+
### 搜索模拟
59+
60+
根据题意,我们可以按天进行模拟,设计函数 `getCnt` 用于返回当天会被安装的防火墙数量,在 `getCnt` 内部我们会进行如下操作:
61+
62+
* 找出当天「对未感染区域的威胁最大」的区域,并将该区域进行隔离(将 1ドル$ 设置为 $-1$);
63+
* 对其他区域,进行步长为 1ドル$ 的感染操作。
64+
65+
考虑如何实现 `getCnt`:我们需要以「连通块」为单位进行处理,因此每次的 `getCnt` 操作,我们先重建一个与矩阵等大的判重数组 `vis`,对于每个 $g[i][j] = 1$ 且未被 $vis[i][j]$ 标记为 `True` 的位置进行搜索,搜索过程使用 `BFS` 实现。
66+
67+
**`BFS` 过程中,我们除了统计该连通块所需要的防火墙数量 $b$ 以外,还需要额外记录当前连通块中 1ドル$ 的点集 `s1`(简称为原集,含义为连通块的格子集合),以及当前连通块相邻的 0ドル$ 的点集 `s2`(简称为扩充集,含义为将要被感染的格子集合)。**
68+
69+
根据题意,在单次的 `getCnt` 中,我们需要在所有连通块中取出其 `s2` 大小最大(对未感染区域的威胁最大)的连通块进行隔离操作,而其余连通块则进行扩充操作。
70+
71+
因此我们可以使用两个变量 `max``ans` 分别记录所有 `s2` 中的最大值,以及取得最大 `s2` 所对应连通块所需要的防火墙数量,同时需要使用两个数组 `l1``l2` 分别记录每个连通块对应的「原集」和「扩充集」,方便我们后续进行「隔离」和「感染」。
72+
73+
Java 代码:
74+
```Java
75+
class Solution {
76+
int[][] g;
77+
int n, m, ans;
78+
int[][] dirs = new int[][]{{1,0},{-1,0},{0,1},{0,-1}};
79+
boolean[][] vis;
80+
int search(int _x, int _y, Set<Integer> s1, Set<Integer> s2) {
81+
int ans = 0;
82+
Deque<int[]> d = new ArrayDeque<>();
83+
vis[_x][_y] = true;
84+
d.addLast(new int[]{_x, _y});
85+
s1.add(_x * m + _y);
86+
while (!d.isEmpty()) {
87+
int[] info = d.pollFirst();
88+
int x = info[0], y = info[1];
89+
for (int[] di : dirs) {
90+
int nx = x + di[0], ny = y + di[1], loc = nx * m + ny;
91+
if (nx < 0 || nx >= n || ny < 0 || ny >= m || vis[nx][ny]) continue;
92+
if (g[nx][ny] == 1) {
93+
s1.add(loc);
94+
vis[nx][ny] = true;
95+
d.addLast(new int[]{nx, ny});
96+
} else if (g[nx][ny] == 0) {
97+
s2.add(loc);
98+
ans++;
99+
}
100+
}
101+
}
102+
return ans;
103+
}
104+
int getCnt() {
105+
vis = new boolean[n][m];
106+
int max = 0, ans = 0;
107+
// l1: 每个连通块的点集 s2: 每个连通块的候选感染点集
108+
List<Set<Integer>> l1 = new ArrayList<>(), l2 = new ArrayList<>();
109+
for (int i = 0; i < n; i++) {
110+
for (int j = 0; j < m; j++) {
111+
if (g[i][j] == 1 && !vis[i][j]) {
112+
// s1: 当前连通块的点集 s2: 当前连通块的候选感染点集
113+
Set<Integer> s1 = new HashSet<>(), s2 = new HashSet<>();
114+
int b = search(i, j, s1, s2), a = s2.size();
115+
if (a > max) {
116+
max = a; ans = b;
117+
}
118+
l1.add(s1); l2.add(s2);
119+
}
120+
}
121+
}
122+
for (int i = 0; i < l2.size(); i++) {
123+
for (int loc : l2.get(i).size() == max ? l1.get(i) : l2.get(i)) {
124+
int x = loc / m, y = loc % m;
125+
g[x][y] = l2.get(i).size() == max ? -1 : 1;
126+
}
127+
}
128+
return ans;
129+
}
130+
public int containVirus(int[][] _g) {
131+
g = _g;
132+
n = g.length; m = g[0].length;
133+
while (true) {
134+
int cnt = getCnt();
135+
if (cnt == 0) break;
136+
ans += cnt;
137+
}
138+
return ans;
139+
}
140+
}
141+
```
142+
TypeScript 代码:
143+
```TypeScript
144+
let g: number[][] = null
145+
let n: number = 0, m: number = 0
146+
let vis: boolean[][] = null
147+
const dirs: number[][] = [[1,0],[-1,0],[0,1],[0,-1]]
148+
function dfs(_x: number, _y: number, s1: Set<number>, s2: Set<number>): number {
149+
let he = 0, ta = 0, ans = 0
150+
let d: Array<number> = new Array<number>()
151+
s1.add(_x * m + _y)
152+
vis[_x][_y] = true
153+
d[ta++] = _x * m + _y
154+
while (he < ta) {
155+
const poll = d[he++]
156+
const x = Math.floor(poll / m), y = poll % m
157+
for (const di of dirs) {
158+
const nx = x + di[0], ny = y + di[1], loc = nx * m + ny
159+
if (nx < 0 || nx >= n || ny < 0 || ny >= m || vis[nx][ny]) continue
160+
if (g[nx][ny] == 1) {
161+
s1.add(loc)
162+
vis[nx][ny] = true
163+
d[ta++] = loc
164+
} else if (g[nx][ny] == 0) {
165+
s2.add(loc)
166+
ans++
167+
}
168+
}
169+
}
170+
return ans
171+
}
172+
function getCnt(): number {
173+
vis = new Array<Array<boolean>>(n)
174+
for (let i = 0; i < n; i++) vis[i] = new Array<boolean>(m).fill(false)
175+
let max = 0, ans = 0
176+
let l1: Array<Set<number>> = new Array<Set<number>>(), l2: Array<Set<number>> = new Array<Set<number>>()
177+
for (let i = 0; i < n; i++) {
178+
for (let j = 0; j < m; j++) {
179+
if (g[i][j] == 1 && !vis[i][j]) {
180+
let s1 = new Set<number>(), s2 = new Set<number>()
181+
const b = dfs(i, j, s1, s2), a = s2.size
182+
if (a > max) {
183+
max = a; ans = b
184+
}
185+
l1.push(s1); l2.push(s2)
186+
}
187+
}
188+
}
189+
for (let i = 0; i < l2.length; i++) {
190+
for (let loc of l2[i].size == max ? l1[i] : l2[i]) {
191+
const x = Math.floor(loc / m), y = loc % m
192+
g[x][y] = l2[i].size == max ? -1 : 1
193+
}
194+
}
195+
return ans
196+
}
197+
function containVirus(_g: number[][]): number {
198+
g = _g
199+
n = g.length; m = g[0].length
200+
let ans: number = 0
201+
while (true) {
202+
const cnt = getCnt()
203+
if (cnt == 0) break
204+
ans += cnt
205+
}
206+
return ans
207+
};
208+
```
209+
* 时间复杂度:最多有 $n + m$ 天需要模拟,每天模拟复杂度 $O(n \times m),ドル整体复杂度为 $O((n + m) \times nm)$
210+
* 空间复杂度:$O(nm)$
211+
212+
---
213+
214+
### 最后
215+
216+
这是我们「刷穿 LeetCode」系列文章的第 `No.749` 篇,系列开始于 2021年01月01日,截止于起始日 LeetCode 上共有 1916 道题目,部分是有锁题,我们将先把所有不带锁的题目刷完。
217+
218+
在这个系列文章里面,除了讲解解题思路以外,还会尽可能给出最为简洁的代码。如果涉及通解还会相应的代码模板。
219+
220+
为了方便各位同学能够电脑上进行调试和提交代码,我建立了相关的仓库:https://github.com/SharingSource/LogicStack-LeetCode
221+
222+
在仓库地址里,你可以看到系列文章的题解链接、系列文章的相应代码、LeetCode 原题链接和其他优选题解。
223+

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /