【问题标题】:Find the number of triples (i, j, k) in array such that A[i] + A[j] = 2 * A[k]求数组中三元组 (i, j, k) 的数量,使得 A[i] + A[j] = 2 * A[k]
【发布时间】:2018-09-16 23:46:49
【问题描述】:

如何在数组中找到连音/对 i、j、k 的数量,使得 a[i] + a[j] = 2 * a[k]。复杂度应该是 O(n * logn) 或 O(n),因为 n

编辑 2(重要):abs(a[i])

编辑: i, j, k 都必须是不同的。

这是我的代码,但是太慢了,复杂度为 O(is n^2 logn)。

#include <bits/stdc++.h>

using namespace std;

int binarna(vector<int> a, int k){
    int n = a.size();
    int low = 0, high = n - 1; 
    bool b = false;

    int mid;
    while(low <= high){
        mid = (low + high) / 2;

        if (a[mid] == k){
            b = true;
            break;
        }

        if (k < a[mid])
            high = mid - 1;
        else
            low = mid + 1;
    }

    if (b)
        return mid;
    else
        return -1;
}


int main()
{
    int n;
    cin >> n;

    vector<int> a(n);
    for (auto& i : a)
        cin >> i;

    sort(a.begin(), a.end());

    int sol = 0;
    for (int i = 0; i < n - 1; ++i){
        for (int j = i + 1; j < n; ++j){
            if ((a[i] + a[j]) % 2)
                continue;
            int k = (a[i] + a[j]) / 2;

            if (binarna(a, k) != -1)
                ++sol;
        }
    }

    cout << sol << '\n';
}

【问题讨论】:

  • i==j==k 是有效答案还是有其他限制?你都尝试了些什么? SO 不是代码编写网站。
  • 看起来你可以使用一些 for 循环。
  • @RichardCritten Values "in array",不仅仅是索引
  • 通过按值将a 传递给函数,你做了很多不必要的复制。
  • 对于每个i != j 计算a[i] + i[j]。如果是奇数,则没有k。否则,搜索数组以找到等于(a[i] + i[j])/2 的元素。如果有,你有一个解决方案。您可能还想限制这个问题:数组元素是唯一的吗?数组是否排序? [您的代码似乎正在尝试使用二进制搜索,这表明数组至少已排序,但问题中未指定]。

标签: c++ math


【解决方案1】:

复杂度不可能比O(N²)好,因为在元素形成一个等差数列的情况下,所有对(i, j)j-i甚至在中间都有一个合适的元素,计数是@987654324 @*。

O(N²)的解决方案如下:

  • 对数组进行递增排序;

  • 对于每个i

    • 设置k=i 和每个j&gt;i

      • 递增 k 直到 2 A[k] &gt;= A[i] + A[j]

      • 如果相等则增加计数

对于给定的ijk 单调递增到N,因此操作总数为O(N-i)。这证明了全局行为 O(N²) 的合理性,这是最优的。


*这里有一点微妙之处,因为您可能会通过声称与该论点相矛盾:“我们可以确定该数组在时间O(N) 上形成了一个算术序列,并由此计算出一次计数”。

但是,如果我们有两个长度为N/2 的算术序列而不是单个算术序列,那么即使它们交织在一起,计数的二次行为仍然存在。并且至少有N 方法可以将两个算术序列交织在一起。


如果元素的范围远小于它们的数量,则通过直方图压缩数据是有利的。

三重检测算法稍微简化了一点,因为k 系统地是(i+j)/2。现在每个三元组都计入Hi.Hk.Hj 而不是1。复杂度为O(M²),其中M 是直方图的大小。

【讨论】:

  • 我可能错了,但是 O(n^2) 太慢了。我有一个 n^2 代码(我写的那个),但不要认为这会足够快,因为 n
  • @Morgana:您的代码是 O(N²Log N),而不是 O(N²)。如果你相信我的论点,在最坏的情况下不可能比 O(N²) 更快。
  • 我忘了要说一件大事!没看到... abs(a[i)]
  • @Morgana:是的,有了这些信息,问题就大不相同了,解决方案可能是基于直方图的。
  • 我不太确定如何使用直方图?
【解决方案2】:

让我们调用D - 数组中不同值的总数。如果abs(a[i]) &lt;= 10^3,则数组中的不同值不能超过2*10^3。这意味着如果你有点聪明,你的算法的复杂度将变为O(D^2*log(D))O(N*log(N)) 的最小值,这比 O(N^2*log(N)) 好得多,如果你使用 Yves 建议的智能算法,你得到的最小值是 O(D^2*log(D))O(N*log(N))

显然O(N*log(N)) 来自排序,你无法避免它,但即使对于N = 10^5 也可以。那么如何在算法的主体部分将N减少到D呢?这并不难。您需要用元组数组(value, count) 替换int 值数组(我们称之为B)。在排序后扫描原始数组很容易得到这样的数组。这个新数组的大小是D(而不是N)。现在你将你的算法或 Yves 改进算法应用于这个数组,但每次你找到一个三元组 (i,j,k) 这样

2*B[k].value == B[i].value + B[j].value

您将总计数器增加

totalCount += B[k].count * B[i].count * B[j].count

为什么会这样?考虑原始的排序数组。当你找到一个三元组(i,j,k) 这样

2*A[k].value == A[i].value + A[j].value

您实际上找到了 ijk 的 3 个范围,这样每个范围中的值都相等,因此您可以从相应范围中选择任何数字。简单的组合表明上面的公式。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2019-04-19
    • 1970-01-01
    • 2012-10-20
    • 2021-11-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-02-03
    相关资源
    最近更新 更多