PAT乙级1049. 数列的片段和(20)

数列片段和计算
本文介绍了一种计算给定正整数数列的所有可能片段之和的方法。通过分析每个元素在不同片段中出现的次数,得出一个简洁高效的计算公式,并提供了完整的C++实现代码。

题目:

给定一个正数数列,我们可以从中截取任意的连续的几个数,称为片段。例如,给定数列{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
思路:关键是确定每个元素出现几次。
1. 对于数组中第i个元素,其左边有i个元素,右边有(n-i-1)个元素;
2. 因此在每个数列片段中,左边可出现0,1,...,i个元素,右边可出现0,1,2,...(n-i-1)个元素,所以共有(i+1)*(n-i)种组合。
#include<iostream>
#include<iomanip>
using namespace std;
int main() {
    int n, i;
    cin>>n;
    double a[100005], sum=0.00;
    for (i=0; i<n; i++)
        cin>>a[i];
    for (i=0; i<n; i++)
        sum += a[i]*(i+1)*(n-i);
    cout<<setiosflags(ios::fixed)<<setprecision(2)<<sum;
}


### 关于 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、付费专栏及课程。

余额充值