A1104 Sum of Number Segments(20分)PAT 甲级(Advanced Level) Practice(C++)满分题解【子序列数学问题+测试点2注意double带来的误差】

该博客讨论了一道编程题目,涉及对一串正数序列进行连续子序列划分,并计算所有子序列的和。题目中指出,当序列大小达到一定数量级时,直接使用double类型进行累加会导致精度误差。解决方案是将输入的浮点数放大1000倍转换为long long整型进行累加,最后输出时再除以1000并保留两位小数。这种方法有效地避免了由于double类型精度不足造成的计算误差。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Given a sequence of positive numbers, a segment is defined to be a consecutive subsequence. For example, given the sequence { 0.1, 0.2, 0.3, 0.4 }, we have 10 segments: (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) and (0.4).

Now given a sequence, you are supposed to find the sum of all the numbers in all the segments. For the previous example, the sum of all the 10 segments is 0.1 + 0.3 + 0.6 + 1.0 + 0.2 + 0.5 + 0.9 + 0.3 + 0.7 + 0.4 = 5.0.

Input Specification:

Each input file contains one test case. For each case, the first line gives a positive integer N, the size of the sequence which is no more than 105. The next line contains N positive numbers in the sequence, each no more than 1.0, separated by a space.

Output Specification:

For each test case, print in one line the sum of all the numbers in all the segments, accurate up to 2 decimal places.

Sample Input:

4
0.1 0.2 0.3 0.4

Sample Output:

5.00

Thanks to Ruihan Zheng for correcting the test data.

题意分析:

这道题第一反应就是找规律总结公式

先举个例子{1,2,3,4},n=4,(题目里是<1.0的数,我这里用简单整数代替):

(1)1
(2)12
(3)123
(4)1234
(5)2
(6)23
(7)234
(8)3
(9)34
(10)4

每个数轮流做当前分割子序列的首数,其中n=4:

  • 第一个数出现 1* 4 次
  • 第二个数出现 2*(4-1) 次
  • 第三个数出现 3*(4-2) 次
  • 第四个数出现 4*(4-3) 次

再举几个例子,这里不再赘述。规律已经很明了了,即每个数出现的次数为:

该数所在位置序号i * (n - i + 1)

但是要注意的是,仅仅按以上的规律直接运行的话,测试点2通不过,后来看了柳神的题解才知道是double的问题:

问题分析:N比较大时,double类型的值多次累加导致的精度误差,因为输入为十进制小数,存储到double中时,计算机内部使用二进制表示,且计算机的字长有限,有的十进制浮点数使用二进制无法精确表示只能无限接近,在字长的限制下不可避免会产生舍入误差,这些细微的误差在N较大时多次累加会产生较大误差,所以建议不要使用double类型进行多次累加的精确计算,而是转为能够精确存储的整型。

解决方法:尝试把输入的double类型的值扩大1000倍后转为long long整型累加,同时使用long long类型保存sum的值,输出时除以1000.0转为浮点型再输出(相当于把小数点向后移动3位后再计算,避免double类型的小数部分存储不精确,多次累加后对结果产生影响)

代码如下:

#include<bits/stdc++.h>
using namespace std;
int main()
{
	int n;
    double tmp;
    long long sum=0;
	scanf("%d", &n);
	for (int i = 1; i <= n; i++)
	{
		scanf("%lf",&tmp);
		sum += (long long)(tmp*1000) * (n - i + 1) * i;
	}
	printf("%.2f\n", sum/1000.0);
	return 0;
}

运行结果如下:

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值