【问题标题】:Given N people, some of which are enemies, find number of intervals with no enemies给定 N 个人,其中一些是敌人,找出没有敌人的区间数
【发布时间】:2020-11-02 02:45:54
【问题描述】:

一个朋友给我这个问题作为挑战,我试图在 LeetCode 上找到这样的问题,但遗憾的是找不到。

问题

给定从 1 到 N 编号的一行人,以及一组 M 对敌人的列表,找出不包含两个敌人的人的子行总数。

例子: N = 5, enemies = [[3,4], [3,5]]

答案: 9

解释:这些连续的子区间是:

[1,1], [2,2], [3,3], [1,2], [2,3], [1,3], [4,4], [4,5], [5,5]

我的方法

我们将非冲突区间定义为从(包括)[a,b] 的连续区间,在该区间内没有两个人是敌人。

向后工作,如果我知道[1,3] 有一个非冲突区间,就像上面给出的示例一样,我知道这两个数字之间的连续区间数是@​​987654327@,其中n 是间隔。在这种情况下,间隔长度为3,因此在[1,3] 之间(包括)之间存在6 间隔。

扩展这个逻辑,如果我有一个所有非冲突区间的列表,那么答案就是每个区间长度n_i(n_i*(n_i+1))/2 的总和。

然后我需要做的就是找到这些间隔。 这是我卡住的地方。


我真的想不出类似的编程问题。这看起来很相似,但与 leetcode 上的Merge Intervals 问题所要求的相反。在那个问题中,我们被赋予了好的间隔,并被要求将它们组合起来。在这里,我们得到了不好的结果。

有什么指导吗?


编辑:我能想到的最好的:

这行得通吗?

所以让我们将max_enemy[i] 定义为小于特定人i 的最大敌人,其中i 是通常的[1,N]。我们可以在O(M) 时间内生成这个值,只需使用以下循环:

max_enemy = [-1] * (N+1) # -1 means it doesn't exist
for e1, e2 in enms:
    e1, e2 = min(e1,e2), max(e1, e2)
    max_enemy[e2] = max(max_enemy[e2], e1)

然后,如果我们通过保持滑动窗口的人的数组。一旦我们找到一个人i 拥有:max_enemy[i] < i,滑动窗口就会结束。这样我们就知道包括这个人会打破我们的连续间隔。所以我们现在知道我们的区间是[s, i-1],我们可以进行数学计算。我们重置s=i 并继续。

这是如何在视觉上工作的可视化。我们在任意两个敌人之间画一条路径:

N=5, enemies = [[3,4], [3,5]]

1   2   3   4  5
        |   |  |
        -----
        |      |
        --------

EDIT2:我知道这对N=5, enemies=[[1,4][3,5]] 不起作用,目前正在修复,仍然卡住

【问题讨论】:

  • 我无法推理您的示例、解释和答案...为什么答案是 9?
  • 不,它必须是连续的,所以[1,3] 是不可能的。我添加了更多解释
  • 对不起,我在考虑子数组,我猜子数组[1,2,3] 相当于区间[1,3]。将更新问题
  • @Emma 用我的(破碎的)方法更新了问题

标签: algorithm math sliding-window


【解决方案1】:

有一种很酷的视觉方式来查看这个!

让我们看看成对玩家的矩阵,而不是关注这条线。如果ii 和j 是敌人,那么这种敌人的作用正是从考虑中排除(1)这个区间,以及(2)任何严格大于它的区间。因为敌人是对称的,我们不妨只看矩阵的右上半部分和对角线;我们将使用字符

  • X”表示一对是敌人,
  • *”表示一对被一对敌人遮挡,并且
  • %”在下半部分标记为不属于上半部分矩阵。

对于代码中的两个示例,观察它们对应的矩阵:

# intervals:  9                   # intervals:  10

 0   1   2   3   4                 0   1   2   3   4      
------------------------          ------------------------
             *   *   | 0                        *   *   | 0           
 %           *   *   | 1            %           X   *   | 1           
 %   %       X   X   | 2            %   %           X   | 2           
 %   %   %           | 3            %   %   %           | 3           
 %   %   %   %       | 4            %   %   %   %       | 4           

下面提供的幼稚解决方案解决了O(N^2 M)时间和O(N^2)空间中的问题。

def matrix(enemies):
    m = [[' ' for j in range(N)] for i in range(N)]
    for (i,j) in enemies:
        m[i][j] = 'X' #Mark Enemiship
        # Now mark larger intervals as dead.
        for q in range(0,i+1):
            for r in range(j,N):
                if m[q][r] == ' ':
                    m[q][r] = '*'

    num_int = 0
    for i in range(N):
        for j in range(N):
            if(j < i):
                m[i][j] = '%'
            elif m[i][j] == ' ':
                num_int+=1

    print("# intervals: ", num_int)
    return m

为了进一步说服自己,这里是矩阵

  1. 玩家2是自己的敌人,所以有一个障碍,在区间[0,1][3,4]有两个较小的拼图版本,每个都有3个子区间)
  2. 每个玩家都与左边两个人为敌,因此只允许长度(1 或 0)间隔(其中有 4+5=9 间隔)
# intervals:  6                   # intervals:  9

 0   1   2   3   4                 0   1   2   3   4      
---------[===========+              --------[============+
         *   *   *  || 0                    X   *   *  || 0           
 %       *   *   *  || 1            %           X   *  || 1           
 %   %   X   *   *  II 2            %   %           X  II 2           
 %   %   %           | 3            %   %   %           | 3           
 %   %   %   %       | 4            %   %   %   %       | 4           


复杂性:数学上与排序列表或验证列表是否已排序相同。也就是说,O(M log M) 在最坏的情况下,O(M) 空间进行排序,并且在最好的情况下仍然至少O(M) 时间来识别列表是否已排序。
奖励:这也是一个很好的例子来说明看问题的身份而不是解决问题的力量。这种对问题的看法也会为更聪明的解决方案提供信息。我们显然可以比我上面给出的代码做得更好......

例如,如果我们可以计算未着色点的数量,即覆盖敌人的最小凸多边形的面积,连同两个边界点,我们显然已经完成了。 (找到两个额外的点可以在O(M)时间完成。)现在,这可能不是您可以在睡眠中解决的问题,但幸运的是找到凸包的问题是如此自然,以至于the algorithms used to do it are well known.

特别是,Graham Scan 可以在O(M) 时间内完成,只要我们碰巧获得了成对的敌人,以便对它们的一个坐标进行排序。更好的是,一旦我们在凸包中获得了一组点,就可以通过将其划分为最多M 轴对齐的矩形来计算面积。 因此,如果对敌对进行排序,整个问题可以在O(M) 时间内解决。 请记住,M 可能比N 大得多,我们甚至不需要将 N 个数字存储在一个数组中!这对应于该问题的另一个答案中建议跳过行的算术。

如果它们未排序,则其他 Convex Hull 算法会产生 O(M log M) 运行时间,O(M) 空间,由 @Matt Timmermans's solution 给出。其实这就是一般的下限!这可以用更复杂的几何简化来表示:如果你能解决这个问题,那么你可以计算每个数字的高度之和,乘以它到“新零”的距离,满足j+i = N 的代理。这个总和可用于计算到对角线的距离,这足以在O(M) 时间内对数字列表进行排序——对于对抗性输入,这是在O(M log M) 时间内无法解决的问题。

啊,那么为什么我们可以通过手动执行此集成来获得O(N + M) 解决方案,就像在其他解决方案中明确完成的那样?这是因为如果我们知道 M 个数字属于 N 箱,我们可以通过 Bucket Sort 对它们进行排序。

感谢分享谜题!

【讨论】:

  • 虽然这个答案真的很酷,但您认为有比 O(N^2 * M) 更好的解决方案吗?如果我们要进行这种规模的运行时,我们不妨蛮力解决方案。
  • @QuantumHoneybees 我已经更新了我的答案以反映运行时间分析。
  • @Matt Timmermans 在下面的回答中给出了一种算法,该算法在最坏的情况下与运行时间的 O(M log M) 下限相匹配。
  • @Matt-Timmermans:你对下限有更好的论据吗?
  • @QuantumHoneybees 你买下限分析吗?
【解决方案2】:

你可以在 O(M log M) 时间和 O(M) 空间内解决这个问题。

令 ENDINGAT(i) 为在位置/人 i 处结束的无敌间隔数。这也是以 i 结束的最大无敌间隔的大小。

您寻求的答案是每个人 i 的所有 ENDINGAT(i) 的总和。

令 NEAREST(i) 是在人 i 之前的人 i 最近的敌人。如果我没有前面的敌人,让它为-1。

现在我们可以编写一个简单的公式来计算所有的 ENDINGAT(values):

ENDINGAT(1) = 1,因为只有一个区间以 1 结尾。对于较大的值:

ENDINGAT(i) = MIN( ENDINGAT(i-1)+1, i-NEAREST(i) )

因此,按顺序计算所有 ENDINGAT(i) 是非常容易的,只要我们可以按顺序获得所有 NEAREST(i)。为此,您需要做的就是按最高成员对敌人对进行排序。然后对于每个 i,您可以遍历所有以 i 结尾的对以找到最近的一对。

就是这样——事实证明这很容易。时间主要是排序敌人对所需的 O(M log M) 时间,除非 N 比 M 大得多。在这种情况下,你可以跳过没有前面敌人的人的 ENDINGAT 运行,计算他们对数学求和。

【讨论】:

  • 令 ENDINGAT(i) 为在位置/人 i 处结束的无敌间隔数。这也是以 i 结尾的最大无敌间隔的大小。 你能再解释一下吗?由于某种原因,我无法理解这个词
  • 以 i 结尾的区间是 [i,i]、[i-1,i]、[i-2,i] 等。如果说 i-2 和 i-3 是敌人,那么下一个区间 [i-3, i] 不是无敌的,也不是任何更大的区间。 [i-2,i] 的大小为 3 是结束于 i 的最大无敌区间,其大小是结束于 i 的无敌区间数。
  • 是的,我得到了计算,顺便说一句,你是 100% 正确的,我在我写的所有测试用例上运行了你的逻辑,而不是蛮力方法,它们都通过了! .......我很抱歉在这方面做得很慢,但为什么数学会成功?我不明白如何将间隔的 size 相加,即ENDINGAT's,以某种方式加起来就是子间隔的总数
  • 哦,你接受了另一个答案,所以我以为你已经完成了。无论如何,当你考虑以 i 结尾的间隔时,按照从 [i,i] 开始的长度递增顺序,当你得到一个包含一对敌人的间隔时,你就会停止。有两种情况:1)敌对结束before i。在这种情况下,它还将限制以 i-1 结尾的间隔。否则 2) 敌对结束于 i。 MIN 会考虑这两种情况并选择最有限制的一种。
  • 我们得到最小值,因为这两种情况都是限制,并且适用最严格的限制。我认为您需要手动尝试一些示例。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-21
  • 2016-03-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多