【问题标题】:finding triangulars from array从数组中找到三角形
【发布时间】:2011-08-09 22:41:37
【问题描述】:

给出了由 N 个整数组成的零索引数组 A。三元组 (P, Q, R) 是三角形的,如果并且

A[P] + A[Q] > A[R], 
A[Q] + A[R] > A[P], 
A[R] + A[P] > A[Q]. 

例如,考虑数组 A 使得

A[0] = 10    A[1] = 2    A[2] =  5
A[3] =  1    A[4] = 8    A[5] = 20

三元组 (0, 2, 4) 是三角形的。 写一个函数

int triangle(const vector<int> &A);

给定一个由 N 个整数组成的零索引数组 A,如果该数组存在三角形三元组,则返回 1,否则返回 0。

假设:

N 是 [0..100,000] 范围内的整数; 数组 A 的每个元素都是 [-2,147,483,648..2,147,483,647] 范围内的整数。 例如,给定数组 A 使得

A[0] = 10 A[1] = 2 A[2] = 5 A[3] = 1 A[4] = 8 A[5] = 20 如上所述,该函数应返回 1。给定数组 A 使得 A[0] = 10 A[1] = 50 A[2] = 5 A[3] = 1 该函数应返回 0。 预期的最坏情况时间复杂度:
预期的最坏情况空间复杂度:O(1)

【问题讨论】:

  • 听起来像是功课。到目前为止你在哪里?
  • 其实是功课!我原本打算对它进行排序,但由于 0
  • 为什么在你的问题中使用零索引的 C 样式数组,却要求函数接受向量作为参数? :)
  • 您忽略了时间复杂度要求。如果没有要求,您可以只查看每个可能的 3 个元素集并对它们进行检查,但这是 O(N³)
  • @Jiri:想想当你以当前形式编译这个程序时会发生什么。在堆栈上,您只需为数组分配一个指针。

标签: c++


【解决方案1】:

第一次声明

首先,考虑非正数是没有意义的。如果至少有一个数字是负数或零,您就不可能实现三角形不等式。这很明显,但证据如下:

假设 A、B、C 服从三角不等式,而 C

  • A + C > B。因此 A > B。
  • B + C > A。因此 B > A。

(矛盾)。

第二次申领

假设 A、B、C 服从三角形不等式,而 C 是 A、B、C 中最大的。那么对于 A,B 和 C 之间的每个 A2 和 B2 - 它们也将服从三角不等式。

换句话说:

  • A、B、C 服从三角形不等式。
  • C >= A
  • C >= B
  • C >= A2 >= A
  • C >= B2 >= B
  • 那么 A2,B2,C 也服从三角不等式。

证明很简单,足以明确写出不等式。

这样做的结果是,如果 C 是您想要找到三角不等式的最大数 - 您应该只检查集合中不超过 C 的两个最大数,并检查 A + B > C。

第三次声明

如果 0 = A*2。

证明也很简单:A + B = A*2

算法

  1. 选择 2 个最大的数字 B 和 C (B
  2. 选择不超过 B 的最大数 A,这样
    • A
    • 确保它与 B、C 的元素不同
    • 只考虑正整数。
    • 如果无法选择这样的号码 - 完成。 (没有三角形)。
  3. 检查 A、B、C 是否服从三角不等式。测试 A + B > C。(如果有,则完成)。
  4. 丢弃最大的数 C。代入 C = B,然后 B = A。
  5. 转到第 2 步。

第四次声明

上述算法在最大整数大小上是对数的。换句话说,它在数据类型位数中是线性的。最坏情况的复杂性与输入长度独立有关。因此 - 输入长度为 O(1)。

证明:

在每次迭代(没有找到解决方案)我们有 A 新的 C。这意味着每两次迭代后,最大的数字至少会小 2 倍。

显然,这给了我们迭代次数的上限。假设我们的整数被限制为 31 位(我们忽略负数),而最小有趣的最大 C 是 1,这给了我们不超过 2 * (31 - 1) = 60 次迭代

【讨论】:

  • 第 2 步是线性搜索,除非您可以对数组进行预排序,因为它正在查看所有其他条目。
  • @Dave S:是的,我的意思是算法复杂度是 O(N)。对不起。
  • “数据类型位数”是什么意思?
【解决方案2】:

如果O(N³) 的时间复杂度可以接受,那么下面的伪代码应该可以工作。如果您有更严格的时间复杂度要求,则必须指定它们。

for (P in A){
    for (Q in A){
        for (R in A){
            if(A[P] > 0 && A[Q] > 0 && A[R] > 0){
                if(A[P] > A[R] - A[Q] && A[Q] > A[P] - A[R] && A[R] > A[Q] - A[P]){
                    return 1;
                }
            }
        }
    }
}
return 0;

if 语句背后的原因是这样的:

由于 int 可以是最大 int 的任何值,因此您必须处理溢出问题。如果数组中有两个非常大的整数,将它们加在一起可能会导致奇怪的错误。因此,我们测试它们是否为正,然后重写公式以进行相同的检查,但要进行减法。如果任何值为负数或 0,我们不需要做任何事情,因为:

Assume x <= 0
Assume x+y > z
Assume x+z > y
Then y > z and z > y which is a contradiction

所以没有负值或零值整数将成为三元组的一部分

【讨论】:

  • 很好地处理大整数 (+1)。测试不应该是合取而不是析取吗?
  • @Jiri,是的,你是对的,那是一个错误。我现在编辑了它。
  • 是否保证 long 为更长的 32 位?如果是这样,我会使用 longs 进行不等式比较,因为这是家庭作业并且不需要高效。我也不明白你的小证明;第二个和第三个假设的动机是什么,为什么这个矛盾告诉我们“如果任何值为负或 0,我们不需要做任何事情”。事实上,我有一个反例来反驳你的说法:如果数组有一个负数,函数需要返回一个值或 0 或 1,因此函数需要做 something。跨度>
  • 好的,我现在明白了。您证明了如果一个元素是非正数,那么它不能是三角形三元组的一部分,这证明了 'if(A[P] > 0 && A[Q] > 0 && A[R] > 0)' 部分你的代码。但我不会声称“我们不需要做任何事情”。
  • @DavidGrayson 我的意思是在循环内不需要对负值进行任何特殊处理。
【解决方案3】:

排序会很酷,但 const 向量和 O(1) 空间要求不允许这样做。

(因为这是作业)一些提示:三角数彼此接近。

【讨论】:

  • 我很确定O(N**2) 解决方案是可能的。
【解决方案4】:

提示:如果您只选择数组的两个成员,那么三角形三元组的第三个成员的可能值有什么限制?超出这些限制的任何数字都可以立即被拒绝。

【讨论】:

    【解决方案5】:

    有很多就地排序;使用其中一个对数组进行排序 - 比如说对较小的数组进行梳排序(时间复杂度 O(N^2))或堆排序(复杂度 O(N log(N))。

    一旦你对数组进行了排序,问题应该是是否存在一组 3 个数字,其中 A[X] &gt; (A[X-1] + A[X+1]) / 2 即中间数字大于前后数字的平均值(遗憾的是,这是一个猜测,我没有真实的基础 - 如果它不正确,我希望有人纠正我,但应该有一些好方法来重新定义“三角形”要求,以便更容易检查)。

    现在您只需对已排序的数组进行 O(1) 次迭代以检查条件是否为真,因此总体复杂度将是排序算法的复杂度(最佳情况 N logN)

    【讨论】:

    • 我们无法进行就地排序,因为向量是 const。
    • 而且你不能复制,因为那会违反空间限制。
    • 完全错过了它的常量向量,排序会容易得多:(
    • 这会以数字 1、5 和 8 失败。5 > 4.5(即 8+1/2),但 1+5
    猜你喜欢
    • 2018-01-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多