问题陈述: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;
}
谢谢!
答案 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
我们在左侧10
次3
次,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
以处理更大的集合,而不会使运行总计被切断。