LeetCode每日一题(Making A Large Island)

You are given an n x n binary matrix grid. You are allowed to change at most one 0 to be 1.

Return the size of the largest island in grid after applying this operation.

An island is a 4-directionally connected group of 1s.

Example 1:

Input: grid = [[1,0],[0,1]]
Output: 3
Explanation: Change one 0 to 1 and connect two 1s, then we get an island with area = 3.

Example 2:

Input: grid = [[1,1],[1,0]]
Output: 4
Explanation: Change the 0 to 1 and make the island bigger, only one island with area = 4.

Example 3:

Input: grid = [[1,1],[1,1]]
Output: 4
Explanation: Can’t change any 0 to 1, only one island with area = 4.

Constraints:

n == grid.length
n == grid[i].length
1 <= n <= 500
grid[i][j] is either 0 or 1.


第一层:

  1. 我们需要将相连的1组成一个个独立的岛
  2. 挨个检查grid中的0,上下左右不同岛的面积相加,然后取最大值

第二层:

  1. 相连1组合成岛的问题, 给每个1一个独立的编号,然后重复遍历grid, 将每个元素与其上下左右的邻居进行对比,如果邻居们的最大编号比它的编号大,那就把它的编号改成这个最大编号。同时我们用一个数组记录每个编号对应的元素数量(面积), 修改元素编号的时候同时要对这个数组进行修改, 这样最后我们确定岛面积的时候直接查阅这个数组就可以了,不用再进行重新遍历。最终我们会得到一个个独立的拥有一致编号的岛。
  2. 首先我们可以确定的是,最终的答案至少是这些岛中的面积最大值,最极端的情况就是,整个grid中都是1,这样我们就得到了一个面积是n²的岛。其次的情况就是有若干个岛,但是相互之间相隔的不止一个0,这样我们得到的答案就是最大岛的面积+1。最后的情况就是有若干个岛之间只有一个0作为间隔,这样的话,答案就是这些岛的面积加和加1的最大值。但是实际编码的时候不需要做这种判断,我们检查grid中的每个0元素,把它上下左右的邻居编号拿到,然后注意,我们只把不同编号的岛的面积加起来就好了。不是全部相加,比如说一个0,它的左边和上边都跟100号岛接壤,右边和下边都跟200号岛接壤, 这时的面积应该是area[100] + area[200] + 1而不是area[100]+area[100]+area[200]+area[200]+1。还有一种情况就是这个0周边也是0,这也不用单独处理,因为我们的area[0] == 0, 也就是说我们也把0看做是岛,只不过岛的编号是0且面积也是0。

use std::collections::HashSet;
use std::iter::FromIterator;

impl Solution {
    pub fn largest_island(mut grid: Vec<Vec<i32>>) -> i32 {
        let mut counts = vec![0; 250000];
        let mut val = 1;
        grid.iter_mut().for_each(|l| {
            l.iter_mut().for_each(|v| {
                if v == &mut 1 {
                    *v = val;
                    counts[val as usize] = 1;
                    val += 1;
                }
            });
        });
        loop {
            let mut changed = false;
            for i in 0..grid.len() {
                for j in 0..grid[0].len() {
                    if grid[i][j] > 0 {
                        let left = if j > 0 { grid[i][j - 1] } else { 0 };
                        let right = if j < grid[0].len() - 1 { grid[i][j + 1] } else { 0 };
                        let top = if i > 0 { grid[i - 1][j] } else { 0 };
                        let bottom = if i < grid.len() - 1 { grid[i + 1][j] } else { 0 };
                        let max = left.max(right).max(top).max(bottom);
                        if max > grid[i][j] {
                            counts[grid[i][j] as usize] -= 1;
                            counts[max as usize] += 1;
                            grid[i][j] = max;
                            changed = true;
                        }
                    }
                }
            }
            if !changed {
                break;
            }
        }
        let mut ans = *counts.iter().max().unwrap();
        for i in 0..grid.len() {
            for j in 0..grid[0].len() {
                if grid[i][j] == 0 {
                    let left = if j > 0 { grid[i][j - 1] } else { 0 };
                    let right = if j < grid[0].len() - 1 { grid[i][j + 1] } else { 0 };
                    let top = if i > 0 { grid[i - 1][j] } else { 0 };
                    let bottom = if i < grid.len() - 1 { grid[i + 1][j] } else { 0 };
                    let vals: HashSet<i32> = HashSet::from_iter(vec![left, right, top, bottom].into_iter());
                    let count = vals.into_iter().map(|k| counts[k as usize]).sum::<i32>() + 1;
                    ans = ans.max(count);
                }
            }
        }
        ans
    }
}

后记:
个人感觉,把自己的思路写明白远远比解题难得多,很多东西自己脑子里面想起来很简单,但是要想准确表达出来还是要费些功夫,以前我一直仅想把这个微博作为自己的自留地,记录意义要远大于分享。写下的东西不需要太详细自己能看懂就可以了,更多的是想留下自己的足迹。但是前几天看了一篇文章,说程序员的沟通和表达的能力其实与专业知识一样重要,毕竟在这个开源的时代已经没有几个人能凭一己之力创造奇迹了。所以我想或许这里可以变成一个锻炼自己的平台。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值