加载中...
返回

难题本 | LeetCode959. 由斜杠划分区域

题目链接:https://leetcode-cn.com/problems/regions-cut-by-slashes/

令我思考很久的一道题,最后还是学着官方题解做出来的([1])。思考过程中也想过并查集,然而并没有想到题解中的做法;想到过求一个图中环路的个数,然而不会。

题目

在由 1 x 1 方格组成的 N x N 网格 grid 中,每个 1 x 1 方块由 /\ 或空格构成。这些字符会将方块划分为一些共边的区域。

(请注意,反斜杠字符是转义的,因此 \ 用 “\\” 表示。)。

返回区域的数目。

分析

将单元格进一步细分,便于并查集操作。

每个单元格对角点互连,即可分成四个小三角形(图1),根据本格中的字符是/还是\,合并不同的小三角形。

  • 格内合并

    • 当本格字符为空格时,所有小三角形处于同一个区域中,全部合并;
    • 当本格字符为/时,合并0号和3号,合并1号和2号,且这两个区域不互通;
    • 当本格字符为\时,合并0号和1号,合并3号和2号,且这两个区域不互通。
  • 格间合并

    • 格间合并是指单元格之间的小三角形合并。无论本单元格中的字符是什么,当前单元格的1号三角形和右边单元格的3号永远处于同一个区域,当前单元格的2号下边单元格的0号永远处于同一个区域。同理考虑左边和上边单元格。
    • 我们要遍历所有的单元格,故格间合并只需要顾及左右两侧邻居之一、上下两侧邻居之一即可;例如对于每个单元格只考虑与其右边单元格和下边单元格的合并,每个单元格都如此,则所有单元格都能正常完成合并。
  • 最终操作

    • 遍历每个单元格,按照字符进行格内合并;如果右边和下边有邻居就进行格间合并。这样最后剩下的独立并查集个数就是最终划分得到的区域数。
    • 这种方式的关键在于格间合并,无论格内字符是什么,格间的联通总能使得离散的区域聚到一起。

模拟

考虑对于示例4:

  • 输入:["/\\","\\/"]
  • 输出:5

网格如下:

我们从左上到右下进行遍历。

首先第一个单元格进行格内合并:

第一个单元格的格间合并,从此图中就不难看出格间合并的规律与格内字符无关。

第二个单元格的格内合并:

格间合并,此时它已经没有右邻居:

第三、第四个单元格分别格内、格间合并,最终得到的各个独立集合如下:

AC代码

将上面的模拟转化成代码,其关键在于单元格和小三角形的表示。我们将二维的网格拉直,则一个坐标(x, y)的单元格位于一维数组中的n * x + y处;每个单元格分为4个小三角形,故一个单元格占据4个数组空间。

最终,坐标(x, y)的单元格位于4 * (n * x + y)4 * (n * x + y) + 3的下标位置。

则最终的一维数组即并查集需要开辟的大小为4 * n * n

class UnionFind {
private:
    int f[10000];
    int cnt;
public:
    UnionFind(int n)
    {
        for (int i = 0; i < 4 * n * n; i++)
        {
            f[i] = i;
        }
        cnt = 4 * n * n;
    }
    
    int getCnt() { return cnt; }

    int find(int n)
    {
        if (f[n] == n)
            return n;
        f[n] = find(f[n]);
        return f[n];
    }

    void Union(int a, int b)
    {
        int ra = find(a);
        int rb = find(b);

        if (ra == rb)
            return ;
        else if (ra < rb)
            f[rb] = ra;
        else if (ra > rb)
            f[ra] = rb;

        // cout << "Union " << a << " with " << b << endl;
        cnt--;

        return ;
    }
};


class Solution {
public:
    int regionsBySlashes(vector<string>& grid) {
        int n = grid.size();
        UnionFind uf(n);

        for (int x = 0; x < n; x++)
        {
            for (int y = 0; y < n; y++)
            {
                int base = 4 * (x * n + y);  // 0,4,8,...
                /* 格内合并 */
                if (grid[x][y] == ' ')
                {
                    uf.Union(base, base + 1);
                    uf.Union(base, base + 2);
                    uf.Union(base, base + 3);
                }
                else if (grid[x][y] == '/')
                {
                    uf.Union(base, base + 3);
                    uf.Union(base + 1, base + 2);
                }
                else if (grid[x][y] == '\\')
                {
                    uf.Union(base, base + 1);
                    uf.Union(base + 2, base + 3);
                }
                /* 格间合并 */
                if (y < n - 1)  // 还有右邻居
                    uf.Union(base + 1, base + 7);  // 己方1,右邻居3
                if (x < n - 1)  // 还有下邻居
                uf.Union(base + 2, base + 4 * n);  // 己方2,下邻居0
            }
        }

        return uf.getCnt();
    }
};

表现还不错,叉会儿腰。

参考资料

[1] 力扣官方题解.由斜杠划分区域[EB/OL].2021-01-24

https://leetcode-cn.com/problems/regions-cut-by-slashes/solution/you-xie-gang-hua-fen-qu-yu-by-leetcode-s-ztob/

10 comments
Anonymous
Markdown is supported
@mpv945
mpv945commentedover 2 years ago

添加图片,如果使用外部图床的http链接 。图片无法点击放大,你那边怎么解决的?

@SGS4ever
SGS4evercommentedover 2 years ago

@mpv945
添加图片,如果使用外部图床的http链接 。图片无法点击放大,你那边怎么解决的?

我的博客没有使用图床,所以没办法帮到你~

@Celetherin
Celetherincommentedover 2 years ago

您好,我也是使用的stack主题,我在照着您的方法添加返回顶部按钮时,遇到了按钮虽然出现、也能够点击,但无法实现实际上的返回顶部功能的问题,我没有任何的代码知识,不知道您有没有解决方法?
另外,也是想提醒一下其他需要这篇教程的朋友,最新版的stack主题,添加返回按钮的组件应该在layouts/partials/sidebar/right.html, 在layouts/_default/single.html中添加代码会导致出现两个右边栏。

@jsjcjsjc
jsjcjsjccommentedover 2 years ago

请教一下博主,如何优雅的给stack主题添加广告哈?
我只想在左或者右侧边栏底部,或者每篇文章底部添加一个小小的广告,但是默认似乎的满屏广告哈~~
感谢

@SGS4ever
SGS4evercommentedover 2 years ago
@ClimbingMouse
ClimbingMousecommentedalmost 2 years ago

你好,按照你的方法设置页面载入动画,这个动画不会停止咋办啊

@46fafa
46fafacommentedalmost 2 years ago

博主你好,请问一下主页布局修改哪里的代码如何作用于整个网页,我发现修改后的布局只存在主页和前两篇文章,其他部分还是没修改的样子

@4kohakunushi
4kohakunushicommentedover 1 year ago

你好,关于左侧栏图标高亮我这里存在一些问题想请教你。我取消了原本主页直接抓取post的内容在中间显示的版块,这个部分改成了其他东西,与此同时新增了一个抓取post信息的与links、search等目录并列的一个目录,现在的问题是这些部分虽然都能正常显示,但是对应的抓取post的那个目录无法选中以后高亮,应该修改增加什么才能让它也可以选中后高亮呢?

@SGS4ever
SGS4evercommentedover 1 year ago

首先我只能基于本文使用的Stack版本来尝试解答,因为没看过当前的Stack主题的代码~
我重新翻了下此前写的关于高亮的内容,理论上只要你的post页面的标题在menu配置中即可高亮。如果post页面是你站点的根路径,那应该可以参考我的文章里写的方法,修改下active的触发逻辑~

@4kohakunushi
你好,关于左侧栏图标高亮我这里存在一些问题想请教你。我取消了原本主页直接抓取post的内容在中间显示的版块,这个部分改成了其他东西,与此同时新增了一个抓取post信息的与links、search等目录并列的一个目录,现在的问题是这些部分虽然都能正常显示,但是对应的抓取post的那个目录无法选中以后高亮,应该修改增加什么才能让它也可以选中后高亮呢?

@sansan-cc
sansan-cccommentedabout 1 month ago

感谢博主的建站帖子,有很大的帮助。

有朋自远方来,不亦说乎?