Pat乙级 1049 数列的片段和

本文详细解析了Pat乙级1049数列问题,通过观察数据规律,推导出求解片段和的公式,并提供了一段使用C++实现的代码示例,特别注意double精度误差问题,采用longlong类型进行缩放存储。

Pat乙级 1049 数列的片段和

题目网址
https://pintia.cn/problem-sets/994805260223102976/problems/994805275792359424

思路

观察给出的数据,得出规律,比如一共10个数,第二个数出现的次数为 2 * (10 - 2 + 1)次,得到公式 result = i * (n - i + 1)。因为double的精度误差问题,用long long对数据进行缩放再存储,最后再除。这里取1000参照了其他人的博客,跟测试数据是相关的,不是一个必然的解决方案。
累加的时候代码中注释掉的一行替换上来就过不了测试点2和测试点3,很奇怪,回头还记得的话再看看。

代码

#include<iostream>
#include<iomanip>

using namespace std;

int main() {
	int n;
	long long sum = 0;
	double f;
	
	cin >> n;
	for (int i = 1; i <= n; i ++) {
		cin >> f;
		sum += (long long)(f * 1000) * i * (n - i + 1);
//		sum += i * (n - i + 1) * (long long)(f * 1000);
	}
	cout << setiosflags(ios::fixed) << setprecision(2) << sum /1000.0 << endl;
	return 0;
} 
### 关于 PAT 乙级 C语言 题目1049 的解题思路与示例代码 #### 解题背景 PAT (Programming Ability Test) 是一项针对编程能力的测试,其乙级考试主要面向初学者中级程序员。题目通常涉及基础算法、数据结构以及逻辑推理等内容。 对于题目 **1049 数列片段**[^4],以下是详细的解析: --- #### 题目描述 给定一个长度为 \(N\) 的序列 \(A_1, A_2, \ldots, A_N\),定义该序列的一个片段为连续的一段子序列 \(A_i, A_{i+1}, \ldots, A_j\) (\(1 \leq i \leq j \leq N\)),并将其所有元素之称为该片段的总。现在需要计算所有可能片段的总,并按照升序排列输出这些不同的总。 --- #### 输入格式 - 第一行包含两个正整数 \(N\) \(M\) (\(1 \leq N \leq 10^5\), \(1 \leq M \leq 10^{18}\))。 - 第二行包含 \(N\) 个整数表示序列中的元素,每个元素范围为 \([-10^9, 10^9]\)。 --- #### 输出格式 - 按照从小到大的顺序输出所有不同片段的总,每行一个数值。 --- #### 思路分析 由于直接枚举所有的片段会带来极高的时间复杂度(约为 \(O(N^2)\)),因此需要优化解决方案。可以采用前缀的思想来降低复杂度至 \(O(N \log N)\),具体如下: 1. 计算数组的前缀 `prefix_sum`,即 `prefix_sum[i] = sum(A[0..i])`。 2. 对于任意一段区间 `[l, r]`,其片段可以通过公式 `sum(l, r) = prefix_sum[r] - prefix_sum[l-1]` 得到。 3. 使用集合存储所有可能的不同片段,最后对集合内的元素进行排序即可。 为了进一步提高效率,在遍历过程中动态维护当前最小值以减少不必要的比较操作。 --- #### 示例代码 以下是一个基于上述思路实现的 C++ 示例代码: ```cpp #include <iostream> #include <vector> #include <set> using namespace std; int main() { int N; long long M; cin >> N >> M; // 输入 N M vector<long long> nums(N); for (auto &num : nums) cin >> num; // 输入序列 set<long long> sums_set; // 存储所有不同的片段 long long current_sum = 0; // 前缀加滑动窗口方法 for (int i = 0; i < N; ++i) { current_sum += nums[i]; sums_set.insert(current_sum); // 插入当前前缀 // 动态更新最小前缀 static long long min_prefix = 0; if (i > 0 && current_sum >= M + min_prefix) { // 如果满足条件则记录差值 sums_set.insert(current_sum - min_prefix); } min_prefix = min(min_prefix, current_sum); // 更新最小前缀 } // 将结果存入向量以便排序 vector<long long> result(sums_set.begin(), sums_set.end()); sort(result.begin(), result.end()); // 排序 // 输出结果 for (const auto& val : result) cout << val << endl; return 0; } ``` --- #### 复杂度分析 - 时间复杂度:构建前缀的时间复杂度为 \(O(N)\),而插入集合的操作平均时间为 \(O(\log S)\),其中 \(S\) 是最终集合大小。总体复杂度接近 \(O(N \log N)\)。 - 空间复杂度:额外空间主要用于存储前缀及去重后的片段,最坏情况下为 \(O(N)\)。 --- #### 注意事项 - 数据规模较大时需注意变量类型的选取,建议使用 `long long` 类型防止溢出。 - 若输入中有负数,则可能出现多个相同的片段,应通过集合自动过滤掉重复项。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值