优化(降低复杂性)。从n边长度计算急性,右边和钝角三角形

时间:2015-10-30 08:56:12

标签: c# .net algorithm time-complexity

编辑1:在约束中将104更改为10 ^ 4。抱歉错误。

问题陈述: 我们有N支。第i个棍子的大小是Ai。我们想知道从一个不同的杆子的每一侧创建的不同类型的三角形的数量。计算锐角三角形,直角三角形和钝角三角形的数量。

输入格式:  第一行包含N.  第二行包含N个整数。第i个数字表示Ai。

约束:

For full score: 3≤N≤5000
For 40% score: 3≤N≤500

适用于所有测试用例

1≤A[i]≤10^4
A[i]<A[i+1] where 1≤i<N

输出格式:  打印3个整数:分别为锐角三角形,直角三角形和钝角三角形。

我的解决方案: 我的代码在给定的时间内运行小n(~500)。它适用于大n(~5000),但我在在线评判中得到超出时间限制的错误。

我的代码:我使用C#作为语言。我希望能够得到同样的解决方案。

using System;

namespace CodeStorm
{
    class CountingTriangles
    {
        public static double square(int x)
        {
            return Math.Pow(x, 2);
        }

        static void Main(string[] args)
        {
            int n = int.Parse(Console.ReadLine());
            string[] A_temp = Console.ReadLine().Split(' ');
            int[] A = Array.ConvertAll(A_temp, Int32.Parse);

            int acute = 0, right = 0, obtuse = 0;
            for (int i = 0; i < n - 2; i++) 
            {
                for (int j = i + 1; j < n - 1; j++) 
                {
                    int k = j + 1;
                    while (k < n && A[i] + A[j] > A[k])
                    {
                        if (square(A[i]) + square(A[j]) == square(A[k]))
                        {
                            right++;
                        }
                        else if (square(A[i]) + square(A[j]) > square(A[k]))
                        {
                            acute++;
                        }
                        else
                        {
                            obtuse++;
                        }
                        k++;
                    }
                }
            }
            Console.WriteLine(acute + " " + right + " " + obtuse);
            Console.ReadLine();
        }
    }
}

https://ideone.com/zbQXE9

上面的代码运行完美,找到了可能的三角形

输入:

6    
2 3 9 10 12 15

输出:

2 1 4

可能的三角形是:

急性三角形 10-12-15,9-10-12

直角三角形 15年9月12日

钝角三角形 2-9-10,3-9-10,3-10-12,9-10-15

我想知道一种更有效的方法来解决这个问题,这样我就可以在n(~5000)的给定时间内执行它。 在我试图找到复杂性之后,我想出了O(n ^ 3)。我对复杂性并不擅长。我可能错了。我想更有效地解决这个问题。

6 个答案:

答案 0 :(得分:6)

您可以通过以下方式改进方法:首先按照长度对所有木棒进行排序。之后迭代每对木棍(即具有方形复杂度的双循环)。对于每一对,执行binary search以找到棒组中的位置,从锐角切换为直角三角形(考虑到您预先选择的两根木棍,第三根作为基础)。然后执行另一个二进制文件,找到将窗体切换为钝角三角形的位置。之后,您将不得不执行另一个二进制搜索,以找到第三个棒很长的位置,以至于您无法与它形成有效的三角形。您将不得不再处理一个案例 - 如果没有直角三角形并且您从锐角直接切换到钝角(这可以通过添加单个if来完成)。

重要的是要注意三角形的类型由与三角形最大边相反的角度确定,因此在上述算法中,您应该只考虑比两个预选棒更大的边长(可以使用另一个二进制)。

总的来说,我提出的方法有复杂度O(N 2 * log(N)),它渐近地比你的算法更好。

答案 1 :(得分:2)

我目前没有看到降低复杂性的方法,但您可以对代码进行一些加速。

  • 预先计算A[i]的方块,而不是一遍又一遍地计算它们
  • 如果长度按照示例中的顺序排序(如果不对它们进行排序),则可以在第一次违反条件A[i] + A[j] > A[k]时立即退出while循环
  • n - 1n -2的值存储在变量中,而不是一遍又一遍地计算它们(可能已由编译器优化)
  • 不计算square(A[i]) + square(A[j])两次,将结果存储在变量中。
  • 更改检查条件的顺序。直角三角形的可能性较小。始终先检查最常见的情况。
  • 使用a * a的整数值可能比square(a)
  • 快得多

在这里你可以看到我的意思(你仍然需要实现TODO部分)

using System;

namespace CodeStorm
{
    class CountingTriangles
    {
        public static double square(int x)
        {
            return Math.Pow(x, 2);
        }

        static void Main(string[] args)
        {
            int n = int.Parse(Console.ReadLine());
            string[] A_temp = Console.ReadLine().Split(' ');
            int[] A = Array.ConvertAll(A_temp, Int32.Parse);
            // TODO: sort A[]  (if it is not already always sorted)
            // TODO: create an array of the square-valuee
            int[] Asquares = ....
            int n_m_2= n-2;
            int n_m_1= n-1;

            int acute = 0, right = 0, obtuse = 0;
            for (int i = 0; i < n_m_2; i++) 
            {
                for (int j = i + 1; j < n_m_1; j++) 
                {
                    int k = j + 1;
                    int AiPlusAj = A[i] + A[j];
                    while (k < n )
                    {
                        if(AiPlusAj <= A[k]){
                          break; 
                        }

                        int squareSum= Asquares[i] + Asquares[j];
                        else if (squareSum > Asquares[k])
                        {
                            acute++;
                        }
                        else if (squareSum < Asquares[k])
                        {
                            obtuse++;
                        }
                        else
                        {
                            right++;
                        }
                        k++;
                    }
                }
            }
            Console.WriteLine(acute + " " + right + " " + obtuse);
            Console.ReadLine();
        }
    }
}

答案 2 :(得分:2)

只是一个小提示:Math.Pow()在计算方块方面相当慢。您应该修改方法方块:

public static double square(int x)
{
    return x * x;
}

答案 3 :(得分:2)

这可以及时解决O(N²)。如有必要,请逐渐对大小进行排序。

让三角形的边是a = A[k]b = A[j]c = A[i]a < b < c,以确保唯一性。要使三角形成为可能,必须验证约束c < a + b。然后根据关系c² >< a² + b²将三角形归类为急性/右/钝。

让我们选择c = A[i]。然后在平面(a, b)a水平)中,约束a < b < c定义了可能三角形的域,它本身就是一个三角形(图中的绿色),它由圆圈遍历等式c² = a² + b²

enter image description here

请按以下步骤操作:

  • b = c = A[j] = A[i]开始并循环

    • 对于给定的b值,找到验证a的最大a + b < c(让a = A[k0]),{{1} (a² + b² < c²)和a = A[k1]a < b)。这些对应于水平方向与斜边和圆弧的交叉点。

    • 获取下一个较小的a = A[k2] = A[j]并更新三个b = A[j]。 (你是&#34;交叉影线&#34;三角形中的两个区域。)

  • 直到您到达a

每种类型的三角形的数量由解的数量的三个和给出,即,如果满足等式,则b < c / 2k1 - k01

循环的代价是线性的,k2 - k1,单调性(当你减少O(N)时,你增加/减少b总是在同一方向)

重复所有k,我们获得了声明的c = A[i]

答案 4 :(得分:0)

两件事几乎肯定会改善事情:

  1. 使平方更快,使用x * x而不是强制转换为加倍,并调用Pow

  2. i计算一次平方,每j计算一次,每k计算一次。

  3. 可能会改善事情的事情,并可能使事情变得更糟(个人资料):

    1. 处理预先排序的数据。

    2. 计算最终尺寸的阈值(其他两个正方形的平方根),然后与循环中的尺寸进行比较。

    3. 当两个正方形与一个整数正方形相加时,有一个不同的循环(没有值可以与直角情况相匹配)。

答案 5 :(得分:0)

我们可以开始搜索我们中断的第三根棒,因为 O(n 2 的复杂性。 Python(抱歉;应该可以理解),现在已经过测试

acute = 0
right = 0
obtuse = 0
a = sorted(a)  # copy and convert to a list
a.append(2 * a[-1])  # add a sentinel equal to twice the max
for i in range(len(a) - 3):  # exclude the sentinel
    if a[i] <= 0:
        continue
    ka = i + 2
    kr = i + 2
    ko = i + 2
    for j in range(i + 1, len(a) - 2):  # i < j, exclude the sentinel
        ka = max(ka, j + 1)
        while a[i] ** 2 + a[j] ** 2 > a[ka] ** 2:
            ka += 1
        acute += ka - (j + 1)
        kr = max(kr, ka)
        while a[i] ** 2 + a[j] ** 2 == a[kr] ** 2:
            kr += 1
        right += kr - ka
        ko = max(ko, kr)
        while a[i] + a[j] > a[ko]:
            ko += 1
        obtuse += ko - kr