如何降低 C++ 中算法的时间复杂度?

Posted

技术标签:

【中文标题】如何降低 C++ 中算法的时间复杂度?【英文标题】:How can I reduce the time complexity of an algorithm in c++? 【发布时间】:2019-12-05 16:45:20 【问题描述】:

以下代码接受一个整数 t,然后再接受 3 个整数 t 次,并返回您可以从两个不同的整数中减去 1 的最大次数同时,当只剩下 1 个大于 0 的整数时程序停止。问题已经解决了,但是想降低代码的时间复杂度不知道怎么做。

#include <bits/stdc++.h>

using namespace std;

int main() 
long long t, r, g, b, arr[1000], count = 0;
bool isMax=true;
cin >> t;

for (long long i = 0; i < t; i++) 
    cin >> r >> g >> b;
    arr[0] = r;
    arr[1] = g;
    arr[2] = b;
    for (long long j = 0; j < 3; j++) 
        for (long long k = 0; k < 2; k++) 
            if (arr[k] > arr[k + 1]) 
                long long a = arr[k];
                long long b = arr[k + 1];
                arr[k] = b;
                arr[k + 1] = a;
            
        
    
    count = 0;
    if (arr[2] == 1) 
        cout << 1 << endl;
     else if (arr[0] + arr[1] <= arr[2]) 
        cout << arr[0] + arr[1] << endl;
     else 
        while (arr[0] > 0) 
            while (isMax && arr[0] > 0) 
                arr[2]--;
                arr[0]--;
                count++;
                if (arr[2] < arr[1]) 
                    isMax = false;
                
            
            while (!isMax && arr[0] > 0) 
                arr[1]--;
                arr[0]--;
                count++;
                if (arr[1] < arr[2]) 
                    isMax = true;
                
            
        
        while (arr[2] > 0 && arr[1] > 0) 
            arr[2]--;
            arr[1]--;
            count++;
        
        cout << count << endl;
    


如何在不使用所有这些增加时间复杂度的循环的情况下获得相同的输出?

编辑:我不希望我的代码为我重写,这是家庭作业,我想要的只是提示和帮助,这样我就可以降低时间复杂度,我不知道该怎么做。

编辑 2:在我的算法中,我按升序对 3 个数字进行排序,然后使用 while 循环检查最小数字 (s/arr[0]) 是否 > 0。之后,我再使用两个while 循环在最大和中等大小的数字之间交替(分别为 l/arr[2] 和 m/arr[1])并从变量 s 和 l 或 m 中减少(交替)。当 s 变为 0 时,这意味着我可以将 l 和 m 递减,直到其中一个等于 0,然后我打印 count 变量(我每次递减两个变量时递增 count)。

【问题讨论】:

听起来像家庭作业? 而不是向我们展示代码并要求我们为您重写它——因为这是一个关于算法和复杂性的问题——也许你可以向我们描述你的算法并询问如何制作的技巧它更优化?就目前而言,这看起来像是“为我编写代码”的家庭作业问题。 我不想为我重写代码,我想知道如何降低时间复杂度。 想一想:如何才能完全删除最内层的循环,在每次迭代中只增加/减少计数器? 您可以通过使用不同的算法来降低复杂性。相同的算法具有固定的复杂度(假设实现没有搞砸) 【参考方案1】:

我不确定我是否正确理解了这个问题。但如果我这样做了,你可以通过以下方式优化算法:

int count = 0;
int a = 20, b = 10, c = 21;
sort(a, b, c); // Function that sorts the numbers, so that a is the smallest and c is the largest
count += a;  // count = 10
c -= a;  // a = 10, b = 20, c = 11
if(c < b) 
    float diff = b - c; // diff = 9
    float distribute = diff / 2; // distribute = 4.5
    count += b - ceil(distribute); // count = 25

else count += b;

你将不得不这 t 次,然后对计数变量求和,导致复杂度为 t。

【讨论】:

【参考方案2】:

假设您的代码是正确的,您可以准确地检查您的循环在做什么,并以更数学的方式看待它们。

if ( arr[2] == 1 ) 
    cout << 1 << endl;
 else if ( arr[0] + arr[1] <= arr[2] ) 
    cout << arr[0] + arr[1] << endl;
 else 
    while ( arr[0] > 0 ) 
        if ( arr[2] > arr[1] ) 
            long long min = std::min( std::min( arr[0], arr[2] ), arr[2] - arr[1] + 1 );
            arr[0] -= min;
            arr[2] -= min;
            count += min;
         else 
            long long min = std::min( std::min( arr[0], arr[1] ), arr[1] - arr[2] + 1 );
            arr[0] -= min;
            arr[1] -= min;
            count += min;
        

    
    count += std::min( arr[2], arr[1] );
    cout << count << endl;

假设你的程序是正确的,他对我尝试的所有输入产生相同的结果。

【讨论】:

【参考方案3】:

我不确定我是否正确理解了这个问题,但是如果您想知道最大次数可以减去 1 直到从三元素集中的两个元素中达到零,我相信答案应该是相同的就像找到集合的中值元素一样。例如,如果我有一套

10 20 30

如果我总是选择从子集20, 30 中减去,我可以减去 1 的 最大 次是 20,而 最小 将是 10 ,如果我总是选择从子集中减去10, 20

希望这会有所帮助! (假设我没有完全误解这个问题^_^“)

Edit:

在澄清评论之后,我相信您需要做的就是找到非最大元素的总和与最大元素之间的最小值。考虑以下示例:

例如,如果给定集合 80, 10, 210,则问题的答案是 90,因为我们可以从子集 80, 10 中减去 10,留下70, 0, 210,我们可以进一步从子集中减去 70子集70, 210,留给我们0,0,140,我们不能再执行任何操作。 我们执行了 80+10 = 90 次减法 1 在这种情况下,max = 210 和 min+med = 90

另一方面,考虑集合2,2,2。我们可以从子集2,2 中减去 2,留下0,0,2,我们不能再执行任何操作。 在这种情况下,我们执行了 2 次减法 1 Max = 2 和 min+med = 4

最后一个例子:考虑集合2,3,5。我们可以从子集2,3 中减去 2,留下0,1,5,我们可以从子集1,5 中减去 1,得到0,0,4,我们不能再执行任何操作。 在这种情况下,我们执行了 2+3=5 减法 1 Max = 5 和 min+med = 5

如果您继续以这种方式执行示例,您应该能够说服自己解决方案始终为 min(max, min+median)

【讨论】:

你看我一开始也是这么想的,但事实证明 10 20 30 的最大值是 30 因为你可以从子集 10, 30 中减去 10 次,那么数组将是 0, 20, 20 和 u 可以从子集 20, 20 中减去 20 次,将 20 与 10 相加得到 30。 @AlhuthaliHumaid -- 看看你是如何表达你的评论的。是否需要循环才能确定值为 30?还是只是一些直觉思维? @AlhuthaliHumaid 我已根据您的评论编辑了我的答案 @LuisSosa Test your solution here

以上是关于如何降低 C++ 中算法的时间复杂度?的主要内容,如果未能解决你的问题,请参考以下文章

并行化可以降低算法复杂度吗

C++算法恢复训练之时间复杂度

如何计算C++的复杂度?

算法改变世界——《算法之美——隐匿在数据结构背后的原理(C++版)》

用C++函数描述个算法,并求出时间复杂度

程序优化--降低复杂度