1049 数列的片段和-PAT乙级

 题目:

给定一个正数数列,我们可以从中截取任意的连续的几个数,称为片段。例如,给定数列 { 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。

输入格式:

输入第一行给出一个不超过 10​5​​ 的正整数 N,表示数列中数的个数,第二行给出 N 个不超过 1.0 的正数,是数列中的数,其间以空格分隔。

输出格式:

在一行中输出该序列所有片段包含的数之和,精确到小数点后 2 位。

输入样例:

4
0.1 0.2 0.3 0.4

输出样例:

5.00

 

 直接遍历序列会超时,应当找规律,注意每个数字出现的次数。

超时代码:

#include <cstdio>

int main()
{
     int n,step=1;
     double sum=0,a[100010]={};
     scanf("%d",&n);
     for(int i=0;i<n;i++) scanf("%lf",&a[i]);
     while(step<=n){
     for(int i=0;i<n;i++)
     {
          double c=0;
          for(int j=i;j<i+step&&(i+step)<=n;j++){c+=a[j];}
          sum+=c;
     }
     step++;
     }
     printf("%.2f",sum);

     return 0;
}

找规律,可以发现,在例题中,0.1出现1*4次,0.2出现2*3次,0.3出现3*2次,0.4出现4*1次

所有可以有:

#include <cstdio>
#include <cstdlib>
const int N=100010;
double a[N]={};
int main()
{
    int n;
	scanf("%d",&n);
	double sum=0;
    for(int i=1;i<=n;i++)
	{
		scanf("%lf",&a[i]);
		sum+=a[i]*(n-i+1)*i;
	}
	printf("%.2f",sum);
	//system("pause");
	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、付费专栏及课程。

余额充值