数学老师给小明出了一道等差数列求和的题目。但是粗心的小明忘记了一 部分的数列,只记得其中 有N 个整数。
现在给出这 N 个整数,小明想知道包含这 N 个整数的最短的等差数列有几项?
输入的第一行包含一个整数N。
第二行包含 N 个整数A1,A2,⋅⋅⋅,AN。(注意 A1 ∼ AN 并不一定是按等差数列中的顺序给出)
其中,2≤N≤100000,,0≤Ai≤1000 000 000。
输出一个整数表示答案。
输入
5 2 6 4 10 20
输出
10
样例说明: 包含 2、6、4、10、20 的最短的等差数列是 2、4、6、8、10、12、14、16、 18、20。
1.首先我们应该给定一个长度为N的数组, 由于VS编译器不支持变长数组,在这里我选择calloc一个长度为N的数组。
2.其次我们可以想到一个等差数组最短,那么它们的公差肯定是最大的,那么它们的最大公差,应该不大于两个相邻数之差的最小值。
例1
数组为2 5 11 20
相邻差 3 6 9
那么最大公差就应该是3,即组成2 5 8 14 17 20
例2
数组为30 45 70 90
相邻差 15 25 20
那么最大公差就应该是5,即组成30 35 40 45 50 55 60 65 70 75 80 85 90
这样就可以确定一个最大公差的范围。
3.接着再对所有的差值求它们的最大公因数,所得到的最大公因数就是该数组的最大公差。
创建一个N长的数组,vs不支持变长数组,当然绝大部分的测试平台是支持的,这里也可以使用变长数组。
int n;
scanf("%d", &n);
int* pa = (int*)calloc(n, sizeof(int));//数列
int i;
perror("calloc1");
for (i = 0; i < n; i++)
{
scanf("%d", &pa[i]);
}
由于题目中给的条件是数组并非按照等差顺序,这里我们应该先对数组排序一下,因为最后计算等差数组元素个数的时候运用an=a0+d*(k-1)
在这里我用了快排qsort函数,因为oj里面冒泡的时间复杂度太高了,O(n^2)过不去检测。
int compare(const void* a, const void* b)
{
return *(int*)a - *(int*)b;
}
//数列从小到大排序
qsort(pa, n, sizeof(int), compare);
//冒泡O(n2)时间复杂度太高
/*for (i = 0; i < n; i++)
{
for (j = 0; j < n - i - 1; j++)
{
if (pa[j] > pa[j + 1])
{
temp = pa[j];
pa[j] = pa[j + 1];
pa[j + 1] = temp;
}
}
}*/
计算两两之间的差值,并且把他们存到pb数组内,因为有n个数,相邻数之间最多只有n-1个差值。
int* pb = (int*)calloc(n - 1, sizeof(int));//两两之间的差值
perror("calloc2");
for (i = 0; i < n - 1; i++)
{
pb[i] = pa[i + 1] - pa[i];
}
差值从小到大排一下,找到最小的差值,因为前面用到了快排,偷个懒直接得到最小的就是pb[0],也不用重新写循环。
//差值从小到大排序
qsort(pb, n - 1, sizeof(int), compare);
//冒泡O(n2)时间复杂度太高
/*for (i = 0; i < n; i++)
{
for (j = 0; j < n - i - 1; j++)
{
if (pb[j] > pb[j + 1])
{
temp = pb[j];
pb[j] = pb[j + 1];
pb[j + 1] = temp;
}
}
}*/
插值去重(可以省略)一开始觉得去一下重可以减一点复杂度,但是其实影响并不是很大。
//差值去重
int l = 0;
for (i = 0; i < n - 1; i++)
{
if (pb[i] != pb[i + 1])
{
pb[l] = pb[i];
l++;
}
}
计算数组的最大公差。
如果最小差值是0,那么就是个常数列,长度就是n;
如果不是0,那么求所有差值的最大公约数。
思路很简单就是让每个差值对1-pb[0]取余数,如果余数是0,那么就是这个差值的约数,反之则不是,所有差值的约数相加,如果都是0的话那么sum也是0,如果有一个不是0,则表示不是公约数
int min = pb[0];//最小差值,最大公差就在1-min之间
int k = 0;
int sum;
//0,则就是最大公差
if (pb[0] == 0)
{
k = n;
}
//不是0则计算最小公差
else
{
do
{
//求最大公约数
for (j = min; j >= 1; j--)
{
sum = 0;
for (i = 1; i < l; i++)
{
sum = sum + pb[i] % j;
}
if (sum == 0)
{
break;
}
}
} while (sum);
k = 1 + (pa[n - 1] - pa[0]) / j;
}
全题思路就是这样,代码有可以优化的地方大家可以在下面留言。