在给定N个整数数组的情况下,如何计算C ++中的差异总和?

时间:2016-06-19 02:15:56

标签: algorithm dynamic-programming

问题陈述https://www.codechef.com/ZCOPRAC/problems/ZCO13001

我的代码在测试用例4和5上运行时间为2.01秒。我无法弄清楚代码的问题: -

#include<iostream>
#include<cstdio>
#include<algorithm>

using namespace std;

int summation(long long int a[], int n, int count)
{
    long long int sum=0;
    int i;
    if(count==n)
        return 0;
    else
    {           
        for(i=count; i<n; i++)
            sum+=a[i]-a[count];

    }
    return sum+summation(a, n, count+1);
}

int main()
{
    int n, i;
    long long int sum;
    scanf("%d", &n);
    long long int a[n];

    for(i=0; i<n; i++)
        scanf("%lld", &a[i]);
    sort(a, a+n);

    sum=summation(a, n, 0);
    printf("%lld\n", sum);
    return 0;
}

谢谢!

3 个答案:

答案 0 :(得分:1)

首先,当您对数字进行排序时,您处于正确的轨道上,但算法的复杂性为O(n^2)。你想要的是O(n)算法。

我只会给你一个提示,之后你如何使用它取决于你。

让我们以您指定的网站3,10,3,5为例给出示例。您可以对这些元素进行排序以获得3,3,5,10。现在具体的是这个差异总和的要素是什么?它们如下 -

3-3
5-3
10-3
5-3
10-3
10-5

我们的结果应该是(3-3) + (5-3) + ... + (10-5)。让我们区别对待这个表达。

3 - 3
5 - 3
10 - 3
5 - 3
10 - 3
10 - 5

43 - 20

我们通过添加-符号左侧和右侧的元素来实现 现在采用变量sum = 0 您需要进行以下观察 -
您可以在这些个别差异中看到第一个3出现在-符号右侧的次数是多少次? 它出现了3次,所以让我们采取sum = -3*3 = -9

现在,对于第二个3,它会在2符号的右侧显示-次,在左侧显示1次,所以我们得到{{1} }}。将此添加到(1-2)*3 = -3,我们得到sum

对于-12,我们左侧5次,右侧2次。我们得到1。将其添加到(2-1)*5 = 5,我们得到sum

现在-12+5 = -7我们在左侧103次,3*10所以sum = -7+30 = 23这是您的必填答案。现在您需要考虑的是一个数字在-符号的左侧和右侧出现的次数。这可以在O(1)时间内完成,而n元素则需要O(n)次。 你有答案吗?如果您不理解这个答案的任何部分,请告诉我。

答案 1 :(得分:0)

您的代码有效,但有两个问题。

使用递归最终会耗尽堆栈空间。我运行你的代码n = 200000(Code Chef问题的上限)并且堆栈溢出。

我将递归转换为等效循环。这是第二个问题 - 需要很长时间。它正在进行2*10^5 * (2*10^5 - 1) / 2个周期,大致为2*10^10。假设处理器每秒可以运行10 ^ 9个周期,那么你需要20秒钟。

要解决时间问题,请在团队力量值中查找重复项。而不是每次在输入中显示时添加相同的强度值(val),添加一次并记录它的找到次数(dup)。然后,在计算货币对(i,j)的贡献时,将a[i].val - a[j].val乘以此组合在原始输入中出现的次数,这是两个重复值a[i].dup * a[j].dup的乘积。

这是修改后的代码,使用Strength结构来保存强度值&amp;它发生的次数。我没有方便的输入文件,因此使用范围为1,100的随机数生成器。通过仅循环唯一强度值,循环总数大大减少。

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<random>

using namespace std;

int codechef_sum1(long long int a[], int n, int count)
{
    long long int sum = 0;
    int i;
    if (count == n)
        return 0;
    else
    {
        for (i = count; i<n; i++)
            sum += a[i] - a[count];

    }
    return sum + codechef_sum1(a, n, count + 1);
}

int codechef_sum2a(long long int a[], int n)
{
    long long int sum = 0;
    for (int i = 0; i < n; i++)
    for (int j = 0; j < i; j++)
        sum += (a[i] - a[j]);
    return sum;
}

struct Strength
{
    long long int val;
    int dup;
    //bool operator()(const Strength& lhs, const Strength & rhs) { return lhs.val < rhs.val; }
    bool operator<(const Strength & rhs) { return this->val < rhs.val; }
};


int codechef_sum2b(Strength a[], int n)
{
    long long int sum = 0;
    for (int i = 0; i < n; i++)
    {
        for (int j = 0; j < i; j++)
            sum += (a[i].val - a[j].val) * (a[i].dup * a[j].dup);
    }
    return sum;
}

int codechef_sum_test(int n)
{
    std::default_random_engine generator;
    std::uniform_int_distribution<int> distr(1, 100);
    auto a1 = new long long int[n];
    auto a2 = new Strength [n];
    int dup = 0, num = 0;

    for (int i = 0; i < n; i++)
    {
        int r = distr(generator);
        a1[i] = r;
        int dup_index = -1;
        for (int ii = 0; ii < num; ii++)
        {
            if (a2[ii].val == r)
            {
                dup++;
                dup_index = ii;
                break;
            }
        }
        if (dup_index == -1)
        {
            a2[num].val = r;
            a2[num].dup = 1;
            ++num;
        }
        else
        {
            ++a2[dup_index].dup;
        }
    }
    sort(a1, a1 + n);
    sort(a2, a2 + num);

    auto sum11 = codechef_sum1(a1, n, 0);
    auto sum12 = codechef_sum2a(a1, n);
    auto sum2 = codechef_sum2b(a2, num);
    printf("sum11=%lld, sum12=%lld\n", sum11, sum12);
    printf("sum2=%lld, dup=%d, num=%d\n", sum2, dup, num);
    delete[] a1;
    delete[] a2;

    return 0;
}

void main()
{
    codechef_sum_test(50);
}

答案 2 :(得分:0)

这是我的解决方案,使用更快的算法。

以下所有内容都是剧透,以防你想自己解决。

-

long long int summation_singlepass(long long int a[], int n)
{
    long long int grand_total=0;
    long long int iteration_sum, prev_iteration_sum=0;
    int i;

    for (i = 1; i < n; i++) {
        iteration_sum = prev_iteration_sum + i * ( a[i] - a[i-1] );
        grand_total += iteration_sum;
        prev_iteration_sum = iteration_sum;
    }

    return grand_total;
}

-

要弄清楚算法,请采取一些简单但有意义的案例。然后自己一步一步地完成它们。这通常会提供很好的见解。

例如:1,3,6,6,8(排序后)

系列中的第三个元素。它与以前元素的差异总和是: (6-1) + (6-3) = 8

系列中的第四个元素。没变!与以前元素的差异总和是: (6-1) + (6-3) + (6-6) = 8

系列中的第五个元素。与第三和第四的公式相比,模式出现。与以前元素的差异总和是: (8-1) + (8-3) + (8-6) + (8-6) = 16

因此,对于该系列中的每个先前元素,它是额外的2。 2是我们当前元素(8)和前一个元素(6)之间的差异。

概括这种效果。将当前迭代总和导出为the previous iteration sum + (i - 1) * ( a[i] - a[i-1] )。其中i是我们当前(1-based)系列中的位置。

请注意,与我上面的编写方式相比,代码在代码中看起来略有不同。这是因为在C ++中我们正在使用基于0的数组索引。

此外 - 如果您想继续调整在OP中发布的解决方案,请将求和的函数返回值更改为long long int以处理更大的集合,而不会使运行总计被切断。