算法(C++/图论)

Posted

技术标签:

【中文标题】算法(C++/图论)【英文标题】:Algorithm (C++ / Graph Theory) 【发布时间】:2015-11-10 22:39:17 【问题描述】:

这几天我一直在努力解决一个算法问题,我尝试了很多方法来解决它,但它们不够准确/不够快,所以我指望你 - 我正在寻找提示或任何有用的东西。

所以问题来了,有一个二维的布尔数组

bool array[n][n] (n <= 1000)

您可以假设,它充满了 1 和 0,但总是以矩形分组,如下所示:

11100
11100
00001
11100

该算法可以将两个 0 变为 1 并形成尽可能大的形状(形成的形状不必是矩形)并返回形成该形状的 1 的数量。不计算对角线连接。

例如:

101
010
101

应该返回 7。问题是,这个算法应该尽可能快地工作,假设 1000x1000 数组的 1-2 秒将是上边界。所以我尝试过:

    首先,我将正方形的正方形分组并形成一个数组,其中包含它们的大小和角的 X、Y。然后我在检查它们之间的关系,但是很难有效地找到潜力最大的组(尤其是当给定的数组就像一个棋盘时)。我只是一个接一个地检查组,检查其相邻的组,然后检查下一个组,以便放置第二个额外的组。这就像蛮力,所以检查大约 500 000 个(对于 1000x1000 棋盘)组实在是太多了。

    我尝试的另一种方法是为每个零创建一个包含其邻居的数组,但找到另一组的非常不理想,再次,这是蛮力。

如果我的英语有任何错误,我很抱歉,我不是母语人士。那么你对我有什么建议,有任何算法或类似问题的链接吗?也许有人会写一个(伪)代码?有什么可以帮忙的,我将不胜感激。

【问题讨论】:

有些事情还不清楚。你说的那些“总是按矩形分组”是什么意思?给定 任何 1 和 0 数组,总是可以将 1 分成(可能是触摸的,可能是 1x1)矩形。 我是一个非常擅长算法的人,很想尝试一下。听起来像是我为了好玩而做的那种问题,但我无法掌握你想要做什么。为什么你的“例如”返回 7?你的第一个示例数组有一个孤立的 1,所以矩形可以像单个布尔值一样小,数组中的单个单元格? 什么是“形状”?如果两个 1 直接相邻,它们是否属于同一个“形状”? (这将被称为连接组件。)如果是这样,是否允许对角连接? 我想我明白了。不计算对角线连接,因此以他为例,您可以更改第 1 行、第 2 列和第 3 行第 2 列的零,然后您会得到一个由 7 个零组成的 H 形。您还可以更改第 2 行、第 1 列和第 2 行第 3 列,并且您还将获得 7 个 H 形状。但是如果你改变了第 1 行第 2 列和第 2 行第 1 列,你只会得到一个有 6 个三角形的三角形,所以不是最佳的(不是最大数量)。 问题,首先,一个矩形的所有矩形是否总是与其他矩形断开连接,或者是否有可能有相邻的矩形? 【参考方案1】:

我首先想到的是蛮力。但是 500,000 x 500,000 个包含零的单元格确实太慢了。

然后我想到了这个:对于每个包含零的单元格,通过将其设置为 1 来计算您可以加入多少个 1。创建一个名为 OnTurning 的对象来表示此操作。从最大的地区向下排列。然后对于每对 OnTurnings,按照它们区域大小总和的粗略顺序,计算出它们的并集大小。当 OnTurnings 的区域大小总和小于您迄今为止找到的最大联合时停止搜索。

【讨论】:

感谢您的回复,500,000 x 500,000 个单元格不是必需的,最多有 1000x1000 个单元格。我不是很喜欢 OOP,所以假设 OnTurning 将是一个 int array[x][y] 如果它不会改变任何东西(如果它是 - 只是说)。你的方法听起来很有希望,我今天要试试,再次感谢你。 @Wolf 不是 500,000×500,000 单元格,而是 1,000×1,000 网格中的 500,000×500,000 对单元格 所以我写了一个你描述的算法,但是在寻找另外两个算法的最佳组合时存在一个大问题。因为两个相似的联合体可以链接相同的联合体,所以链接的总和取决于两个额外的 1 连接了多少联合体。【参考方案2】:

计算连接组件的列表及其大小。对于每个 1-cell,保留一个指向其连接组件的指针。

现在,当您将单元格从 0 翻转到 1 并返回时,您可以使用新的单元格计数快速更新与其相邻的所有组件。此外,您只需要翻转与连接组件相邻的单元格。更进一步,当你翻转一个单元格时,你只需要尝试另一个单元格,如果它与新创建的块相邻。

我认为这将允许一个在单元总数上呈线性的算法。

【讨论】:

感谢您的回答。问题是我需要找到最佳组合,即 1000x1000 棋盘,它必须是蛮力,而且不会那么有效。 “我需要找到最佳组合”。是的,这就是我要说的。 “它必须是蛮力”。如果通过蛮力你的意思是尝试所有 1000×1000×1000×1000/2 对单元格,那么不,它不必是,它不是。 如果我们有一个 n = w*w 单元格的正方形区域,那么我认为以下实例将花费 O(w^3) 时间(即 O(n^(3/2)) time):输入由交替的 1-cells 和 0-cells 行组成。现在有 w^2/2 个 0 单元与连接的组件相邻,因此您的外部循环运行 O(w^2) 次来考虑它们;对于每一个,有 2w 个 0 单元与新形成的块相邻(顶部组件上方的 w 0 单元行和底部组件下方的行),总体为 O(w^3)。 @j_random_hacker 看起来你是对的。也许这可以通过消除“相似”的 0 单元来进一步优化。

以上是关于算法(C++/图论)的主要内容,如果未能解决你的问题,请参考以下文章

图论算法简介

Nebula Graph -- 7. 图论中的路径简介

图论:Tarjan算法

图论基础

图论基础知识总结

2021算法竞赛入门班第七节课图论练习题