【问题标题】:O(n^2) algorithm to find largest 3 integer arithmetic seriesO(n^2) 算法找到最大的 3 个整数算术级数
【发布时间】:2021-06-07 16:08:15
【问题描述】:

问题很简单。给定 N (3 12 + 7 + 2。最大算术级数之间的整数不必相邻,并且它们出现的顺序无关紧要。

一个示例输入是:

6
1 6 11 2 7 12

其中第一个数字是 N(在本例中为 6),第二行是长度为 N 个整数的序列。

输出将是任何 3 整数算术级数的最大和。像这样:

21

因为2、7、12是序列中任意3整数算术级数之和最大的数,且2 + 7 + 12 = 21。也保证序列中存在3整数算术级数。

编辑:构成总和(输出)的数字必须是一个算术级数(常数差),长度为 3 个整数。在样本输入的情况下,(1 6 11) 是可能的算术级数,但它小于 (2 7 12),因为 2 + 7 + 12 > 1 + 6 + 11。因此将输出 21,因为它是更大。

这是我用 C++ 解决这个问题的尝试:

#include <bits/stdc++.h>
using namespace std;
vector<int> results;
vector<int> middle;
vector<int> diff;
int main(){
    int n;
    cin >> n;
    int sizes[n];
    for (int i = 0; i < n; i++){
        int size;
        cin >> size;
        sizes[i] = size;
    }
    sort(sizes, sizes + n, greater<int>());
    for (int i = 0; i < n; i++){
        for (int j = i+1; j < n; j++){
            int difference = sizes[i] - sizes[j];
            diff.insert(diff.end(), difference);
            middle.insert(middle.end(), sizes[j]);
        }
    }
    for (size_t i = 0; i < middle.size(); i++){
        int difference = middle[i] - diff[i];
        for (int j = 0; j < n; j++){
            if (sizes[j] == difference) results.insert(results.end(), middle[i]);
        }
    }
    int max = 0;
    for (size_t i = 0; i < results.size(); i++) {
        if (results[i] > max) max = results[i];
    }
    int answer = max * 3;
    cout << answer;
    return 0;
}

我的方法是使用单独的向量记录中间数字和差异,然后循环遍历向量并搜索中间数字减去差异是否在数组中,然后将其添加到另一个向量。然后找到最大的中间数并乘以 3 得到总和。这种方法使我的算法从 O(n^3) 变为大约 O(n^2)。但是,该算法并不总是每次都能产生正确的输出(而且我想不出一个不起作用的测试用例),并且由于我使用的是单独的向量,所以我得到一个 std::bad_alloc 错误大 N 值,因为我可能使用了太多内存。本题时间限制为每个测试用例 1.4 秒,内存限制为 64 MB。

由于 N 最多只能为 3000,因此 O(n^2) 就足够了。那么这个问题的最优 O(n^2) 解决方案(或更好)是什么?

【问题讨论】:

  • 那么就是说你需要选择的3个数字需要相邻?
  • 您应该能够在O(N) 时间内完成此操作。取元素 0、1、2,求和,存储为最大值。然后获取元素 1、2 和 3,得到总和,与最大值进行比较。如果它更高,则将 max 设置为该新值并记录起始位置。重复直到你到达最后三个元素。
  • 那么如果它们不必相邻,那么简单地选择 3 个最大的数不是问题吗?
  • @StarMan 您对自相矛盾的要求。如果元素不必相邻,那么为什么11 7 12 不是最大和?
  • arithmetic progression 或算术级数是一个成熟的数学概念。

标签: c++ algorithm


【解决方案1】:

因此,此问题的一个简单解决方案是将所有元素放入 std::map 以计算它们的频率,然后遍历等差数列中的第一个和第二个元素,然后在地图中搜索第三个。

迭代需要O(n^2),映射lookupsfind()通常需要O(logn)

include <iostream>
#include <map>
using namespace std;

const int maxn = 3000;
int a[maxn+1];
map<int, int> freq;

int main()
{
    int n; cin >> n;
    for (int i = 1; i <= n; i++) {cin >> a[i]; freq[a[i]]++;} // inserting frequencies

    int maxi = INT_MIN;
    for (int i = 1; i <= n-1; i++)
    {
        for (int j = i+1; j <= n; j++)
        {
            int first = a[i], sec = a[j]; if (first > sec) {swap(first, sec);} //ensure that first is smaller than sec
            int gap = sec - first; //calculating difference
            if (gap == 0 && freq[first] >= 3) {maxi = max(maxi, first*3); } //if first = sec then calculate immidiately
            else
            {
                int third1 = first - gap; //else there're two options for the third element
                if (freq.find(third1) != freq.end() && gap != 0) {maxi = max(maxi, first + sec + third1); } //finding third element
            }
        }
    }
    cout << maxi;
}

输出:21

另一个测试:

6
3 4 5 7 7 7

输出:21

另一个测试:

5
10 10 9 8 7

输出:27

您可以尝试std::unordered_map 尝试进一步降低复杂性。

另见Why is "using namespace std;" considered bad practice?

【讨论】:

  • 您可以通过不检查(third, first, second)(first, second, third) 而只考虑后一种情况来简化这一点。如果两个选项都存在,则后者总是更大。如果只有前者存在,您会在评估 (third, first) 对时找到它。
  • 我认为这非常接近。但是如果序列是 [10,10,9,8,7],这不起作用。它输出 30,而输出应该是 27。因为这个数列中最大的算术级数是 8+9+10(常数差 1)。
  • @StarMan 抱歉,我错过了一个条件 :-) 我又修好了,这是你需要的吗?
【解决方案2】:

3 元素等差数列的总和是中间元素的 3 倍,所以我会搜索中间元素,并从“数组”的“上”端开始搜索(并对其进行排序)。这样,第一个命中是最大的。此外,实际的数组将是一个频率图,因此元素是唯一的,但仍会跟踪任何元素是否有 3 个副本,因为这可能会成为命中(按 0 递增)。
认为最好先创建频率图,然后再对其进行排序,因为这可能会导致排序更少的元素 - 尽管在这种情况下它们将是值和计数对.

function max3(arr){
  let stats=new Map();
  for(let value of arr)
    stats.set(value,(stats.get(value) || 0)+1);
  let array=Array.from(stats);       // array of [value,count] arrays
  array.sort((x,y)=>y[0]-x[0]);      // sort by value, descending
  for(let i=0;i<array.length;i++){
    let [value,count]=array[i];
    if(count>=3)
      return 3*value;
    for(let j=0;j<i;j++)
      if(stats.has(2*value-array[j][0]))
        return 3*value;
  }
}

console.log(max3([1,6,11,2,7,12]));  // original example
console.log(max3([3,4,5,7,7,7]));    // an example of 3 identical elements
console.log(max3([10,10,9,8,7]));    // an example from another answer
console.log(max3([1,2,11,6,7,12]));  // example with non-adjacent elements
console.log(max3([3,7,1,1,1]));      // check for finding lowest possible triplet too

【讨论】:

  • 这看起来只检查由三个相邻元素组成的序列。
  • 如果序列是:[1,2,11,6,7,12],这将不起作用
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2019-12-23
  • 2012-09-26
  • 2012-09-08
  • 1970-01-01
  • 1970-01-01
  • 2014-02-09
  • 2012-02-08
相关资源
最近更新 更多