2101. 引爆最多的炸弹
给你一个炸弹列表。一个炸弹的 爆炸范围 定义为以炸弹为圆心的一个圆。
炸弹用一个下标从 0 开始的二维整数数组 bombs
表示,其中 bombs[i] = [xi, yi, ri]
。xi
和 yi
表示第 i
个炸弹的 X 和 Y 坐标,ri
表示爆炸范围的 半径 。
你需要选择引爆 一个 炸弹。当这个炸弹被引爆时,所有 在它爆炸范围内的炸弹都会被引爆,这些炸弹会进一步将它们爆炸范围内的其他炸弹引爆。
给你数组 bombs
,请你返回在引爆 一个 炸弹的前提下,最多 能引爆的炸弹数目。
示例 1:
输入:bombs = [[2,1,3],[6,1,4]] 输出:2 解释: 上图展示了 2 个炸弹的位置和爆炸范围。 如果我们引爆左边的炸弹,右边的炸弹不会被影响。 但如果我们引爆右边的炸弹,两个炸弹都会爆炸。 所以最多能引爆的炸弹数目是 max(1, 2) = 2 。
示例 2:
输入:bombs = [[1,1,5],[10,10,5]] 输出:1 解释: 引爆任意一个炸弹都不会引爆另一个炸弹。所以最多能引爆的炸弹数目为 1 。
示例 3:
输入:bombs = [[1,2,3],[2,3,1],[3,4,2],[4,5,3],[5,6,4]] 输出:5 解释: 最佳引爆炸弹为炸弹 0 ,因为: - 炸弹 0 引爆炸弹 1 和 2 。红色圆表示炸弹 0 的爆炸范围。 - 炸弹 2 引爆炸弹 3 。蓝色圆表示炸弹 2 的爆炸范围。 - 炸弹 3 引爆炸弹 4 。绿色圆表示炸弹 3 的爆炸范围。 所以总共有 5 个炸弹被引爆。
提示:
1 <= bombs.length <= 100
bombs[i].length == 3
1 <= xi, yi, ri <= 105
原站题解
javascript 解法, 执行用时: 98 ms, 内存消耗: 56.1 MB, 提交时间: 2024-07-22 09:08:55
/** * @param {number[][]} bombs * @return {number} */ var maximumDetonation = function(bombs) { const n = bombs.length; // 判断炸弹 u 能否引爆炸弹 v const isConnected = (u, v) => { const dx = bombs[u][0] - bombs[v][0]; const dy = bombs[u][1] - bombs[v][1]; return bombs[u][2] * bombs[u][2] >= dx * dx + dy * dy; }; // 维护引爆关系有向图 const edges = new Map(); for (let i = 0; i < n; ++i) { for (let j = 0; j < n; ++j) { if (i !== j && isConnected(i, j)) { if (!edges.has(i)) edges.set(i, []); edges.get(i).push(j); } } } let res = 0; // 最多引爆数量 for (let i = 0; i < n; ++i) { // 遍历每个炸弹,广度优先搜索计算该炸弹可引爆的数量,并维护最大值 const visited = Array(n).fill(0); let cnt = 1; const q = [i]; visited[i] = 1; while (q.length > 0) { const cidx = q.shift(); for (const nidx of edges.get(cidx) || []) { if (visited[nidx]) continue; ++cnt; q.push(nidx); visited[nidx] = 1; } } res = Math.max(res, cnt); } return res; };
rust 解法, 执行用时: 19 ms, 内存消耗: 2.3 MB, 提交时间: 2024-07-22 09:08:32
use std::collections::{HashMap, VecDeque}; use std::cmp::max; impl Solution { pub fn maximum_detonation(bombs: Vec<Vec<i32>>) -> i32 { let n = bombs.len(); // 判断炸弹 u 能否引爆炸弹 v let is_connected = |u: usize, v: usize| -> bool { let dx = (bombs[u][0] - bombs[v][0]) as i64; let dy = (bombs[u][1] - bombs[v][1]) as i64; (bombs[u][2] as i64) * (bombs[u][2] as i64) >= (dx * dx + dy * dy) }; // 维护引爆关系有向图 let mut edges: HashMap<usize, Vec<usize>> = HashMap::new(); for i in 0..n { for j in 0..n { if i != j && is_connected(i, j) { edges.entry(i).or_insert(Vec::new()).push(j); } } } let mut res = 0; // 最多引爆数量 for i in 0..n { // 遍历每个炸弹,广度优先搜索计算该炸弹可引爆的数量,并维护最大值 let mut visited = vec![0; n]; let mut cnt = 1; let mut q = VecDeque::new(); q.push_back(i); visited[i] = 1; while let Some(cidx) = q.pop_front() { for &nidx in edges.get(&cidx).unwrap_or(&vec![]) { if visited[nidx] == 1 { continue; } cnt += 1; q.push_back(nidx); visited[nidx] = 1; } } res = max(res, cnt); } res } }
java 解法, 执行用时: 89 ms, 内存消耗: 42.4 MB, 提交时间: 2023-10-10 23:23:48
class Solution { int[][] bombs; int n; boolean[] bombed; // bombed[i]标记有没有爆炸过该*** static long[][] dis; // 两个***之间的距离平方 public int maximumDetonation(int[][] _bombs) { /* 记忆化DFS 数据范围:1 <= bombs.length <= 100 看题目可以知道***数目比较小,因此可以考虑比较暴力的方法 1.预处理出么任意两个***之间的距离平方,后面可能会用到 2.引爆规则:爆炸半径范围内所有***都会爆炸,换句话说就是:半径平方 >= 距离平方 3.枚举引爆每个***最终能引爆得***数,这个过程用dfs进行模拟,用数组进行记录哪些***被引爆了,最后取最大值 坑点:注意int溢出 */ bombs = _bombs; n = bombs.length; // 预处理距离平方 dis = new long[n][n]; for (int i = 0; i < n; i++) { for (int j = 0; j < n; j++) { int x1 = bombs[i][0], y1 = bombs[i][1], x2 = bombs[j][0], y2 = bombs[j][1]; dis[i][j] = (long) (x1 - x2) * (x1 - x2) + (long) (y1 - y2) * (y1 - y2); } } int res = 1; // 枚举从每个***开始爆炸 for (int i = 0; i < n; i++) { // 一开始所有***都没有爆炸 bombed = new boolean[n]; bombed[i] = true; // 从bombs[i]开始炸 dfs(i); // 引爆其他的 // 统计 int cnt = 0; for (int j = 0; j < n; j++) { if (bombed[j]) cnt++; } if (cnt > res) res = cnt; // 记录最大值 } return res; } // 从***i开始引爆 private void dfs(int i) { long r = bombs[i][2]; // 当前***i的爆炸半径 // 枚举所有其他*** for (int j = 0; j < n; j++) { if (bombed[j]) continue; // ***j炸过了就不重复炸了 // ***j还没引爆且位于i的爆炸范围,引爆 if (dis[i][j] <= r * r) { bombed[j] = true; // 引爆***j dfs(j); // 从***j出发引爆其他*** } } } }
java 解法, 执行用时: 127 ms, 内存消耗: 41.6 MB, 提交时间: 2023-10-10 23:23:24
class Solution { // 炸弹引爆最大数量 int max = 1; // 炸弹是否引爆备忘录 boolean[] mem; public int maximumDetonation(int[][] bombs) { mem = new boolean[bombs.length]; for(int i = 0 ; i < bombs.length ; i ++){ // 先引爆一个 dfs(bombs, i); // 所有炸弹置为未引爆状态 Arrays.fill(mem,false); } return max; } private int dfs(int[][] bombs, int i){ if(mem[i]){ return 0; } mem[i] = true; int ret = 1; // 遍历所有炸弹,判断是否会连带引爆 for(int j = 0 ; j < bombs.length ; j ++){ if(mem[j]) continue; if(canBom(bombs,i,j)){ // j爆炸之后,会带动其他的炸弹爆炸,继续dfs ret += dfs(bombs, j); } } max = Math.max(max,ret); return ret; } private boolean canBom(int[][] bombs, int i, int j){ int[] b1 = bombs[i]; int[] b2 = bombs[j]; long x0 = b1[0], x1 = b2[0], y0 = b1[1], y1 = b2[1], r0 = b1[2]; long len = (y1-y0)*(y1-y0) + (x1-x0)*(x1-x0); long r02 = r0 * r0; // 【两点距离的平方】(y1-y0)^2 + (x1-x0)^2 < 【引爆半径的平方】r0^2 则会被引爆 if(len <= r02) return true; return false; } }
python3 解法, 执行用时: 736 ms, 内存消耗: 16.2 MB, 提交时间: 2023-10-10 23:21:46
class Solution: def maximumDetonation(self, bombs: List[List[int]]) -> int: n = len(bombs) # 判断炸弹 u 能否引爆炸弹 v def isConnected(u: int, v: int) -> bool: dx = bombs[u][0] - bombs[v][0] dy = bombs[u][1] - bombs[v][1] return bombs[u][2] ** 2 >= dx ** 2 + dy ** 2 # 维护引爆关系有向图 edges = defaultdict(list) for i in range(n): for j in range(n): if i != j and isConnected(i, j): edges[i].append(j) res = 0 # 最多引爆数量 for i in range(n): # 遍历每个炸弹,广度优先搜索计算该炸弹可引爆的数量,并维护最大值 visited = [False] * n cnt = 1 q = deque([i]) visited[i] = True while q: cidx = q.popleft() for nidx in edges[cidx]: if visited[nidx]: continue cnt += 1 q.append(nidx) visited[nidx] = True res = max(res, cnt) return res
cpp 解法, 执行用时: 112 ms, 内存消耗: 25 MB, 提交时间: 2023-10-10 23:21:26
class Solution { public: int maximumDetonation(vector<vector<int>>& bombs) { int n = bombs.size(); // 判断炸弹 u 能否引爆炸弹 v auto isConnected = [&](int u, int v) -> bool { long long dx = bombs[u][0] - bombs[v][0]; long long dy = bombs[u][1] - bombs[v][1]; return (long long)bombs[u][2] * bombs[u][2] >= dx * dx + dy * dy; }; // 维护引爆关系有向图 unordered_map<int, vector<int>> edges; for (int i = 0; i < n; ++i) { for (int j = 0; j < n; ++j) { if (i != j && isConnected(i, j)) { edges[i].push_back(j); } } } int res = 0; // 最多引爆数量 for (int i = 0; i < n; ++i) { // 遍历每个炸弹,广度优先搜索计算该炸弹可引爆的数量,并维护最大值 vector<int> visited(n); int cnt = 1; queue<int> q; q.push(i); visited[i] = 1; while (!q.empty()) { int cidx = q.front(); q.pop(); for (const int nidx: edges[cidx]) { if (visited[nidx]) { continue; } ++cnt; q.push(nidx); visited[nidx] = 1; } } res = max(res, cnt); } return res; } };
golang 解法, 执行用时: 24 ms, 内存消耗: 6.5 MB, 提交时间: 2023-10-10 23:20:57
func maximumDetonation(bombs [][]int) (ans int) { n := len(bombs) g := make([][]int, n) for i, p := range bombs { for j, q := range bombs { if j != i && (q[0]-p[0])*(q[0]-p[0])+(q[1]-p[1])*(q[1]-p[1]) <= p[2]*p[2] { g[i] = append(g[i], j) // 有向图,从 i 向 j 连边,表示引爆 i 可以引爆 j } } } for i := range g { // 暴力枚举所有起点,跑 DFS 看能访问到多少节点 vis := make([]bool, n) cnt := 0 var dfs func(int) dfs = func(v int) { vis[v] = true cnt++ for _, w := range g[v] { if !vis[w] { dfs(w) } } } dfs(i) if cnt > ans { ans = cnt } } return }