1049 数列的片段和
给定一个正数数列,我们可以从中截取任意的连续的几个数,称为片段。例如,给定数列 { 0.1, 0.2, 0.3, 0.4 },我们有 (0.1) (0.1, 0.2) (0.1, 0.2, 0.3) (0.1, 0.2, 0.3, 0.4) (0.2) (0.2, 0.3) (0.2, 0.3, 0.4) (0.3) (0.3, 0.4) (0.4) 这 10 个片段。
给定正整数数列,求出全部片段包含的所有的数之和。如本例中 10 个片段总和是 0.1 + 0.3 + 0.6 + 1.0 + 0.2 + 0.5 + 0.9 + 0.3 + 0.7 + 0.4 = 5.0。
输入格式:
输入第一行给出一个不超过 105 的正整数 N,表示数列中数的个数,第二行给出 N 个不超过 1.0 的正数,是数列中的数,其间以空格分隔。
输出格式:
在一行中输出该序列所有片段包含的数之和,精确到小数点后 2 位。
输入样例:
4
0.1 0.2 0.3 0.4
输出样例:
5.00
思路:限时200ms,直接暴力遍历求所有片段和,最简也要双重for循环,时间复杂度为O(N2),N = 105 时必然超时。需要寻找数学规律解题。
观察输入样例及题目所给片段,可以发现,0.1被包含了4次,0.2被包含了6次,0.3被包含了6次,0.4被包含了4次,能否从中导出整个数列中,某一特定位置的数在计算中会出现多少次?
对第一个数,从它开始向右增加数形成片段,它自己肯定要被包含n次。故总共n次。
对第二个数,在含第一个数的片段中要出现n-1次,自己为首的片段中也出现n-1次。共2 * (n-1) 次。
对第三个数,在前边的数的片段中出现n-2+n-2次,自己为首的片段中要出现n-2次。共3 * (n-2) 次。
推广,对第 i 个数 (i ≥ 0),它将总共出现 (i + 1) * (n - i) 次。
那么可以只用一重循环,边输入边累加,时间复杂度为O(N),通过。
注意:数据较大,变量类型要使用long double,以防止溢出。对于题目修正后的数据,类型为double应该是无法通过的。
#include <stdio.h>
int main() {
int n;
long double x, sum = 0.0;
scanf("%d", &n);
for (int i = 0; i < n; i++) {
scanf("%Lf", &x);
sum += x * (i + 1) * (n - i); //x必须放在最前面,进行数据类型转换
}
printf("%.2Lf", sum);
return 0;
}