CCF-CSP第36次认证第二题——梦境巡查
官网链接:TUOJ
时间限制: 1.0 秒
空间限制: 512 MiB
相关文件: 题目目录
题目背景
传说每当月光遍布西西艾弗岛,总有一道身影默默守护着居民们的美梦。
参考思路
参考视频:【202412(第36次)CSP真题202412-1,2讲解】 https://www.bilibili.com/video/BV1XkfjYsEsj/?share_source=copy_web&vd_source=9d80e7e6ef9c3f34266696356fc5543f
【刚开始没看懂,后来自己在草稿纸上画图演算突然醒悟了】
参考题解
#include <iostream>
#include <vector>
#include <algorithm>
#include <climits>
using namespace std;
int main () {
int n;
cin >> n;
vector<int> a(n + 1);
for(int i = 0; i <= n; i++) {
cin >> a[i];
}
vector<int> b(n + 1);
for(int i = 1; i <= n; i++) {
cin >> b[i];
}
vector<int> sum(n + 1);//前缀和数组,相当于草稿纸上用到的wi
vector<int> pre_max(n + 1);//前缀和的最大值
sum[0] = a[0];
pre_max[0] = sum[0];
for(int i = 1; i <= n; i++) {
sum[i] = sum[i - 1] + a[i] - b[i];
pre_max[i] = max(sum[i], pre_max[i - 1]);
}
vector<int> suf_max(n + 1);//逆序对前缀和处理后的最大值
suf_max[n] = sum[n];
for(int i = n - 1; i >= 1; i--) {
suf_max[i] = max(sum[i], suf_max[i + 1]);
}
//输出结果
for(int i = 1; i <= n; i++) {
int ans = max(pre_max[i - 1], suf_max[i] + b[i]);
cout << ans << " ";
}
return 0;
}
复习误区:又重复了优化前的问题,逆序求最大值就是为了避免嵌套循环,提高效率
#include <iostream>
#include <vector>
using namespace std;
int main () {
int n;
cin >> n;
vector<int> a(n + 1);
vector<int> b(n + 1);
for(int i = 0; i <= n; i++) {
cin >> a[i];
}
for(int i = 1; i <= n; i++) {
cin >> b[i];
}
vector<int> c(n + 1, 0);//存ai - bi的前缀和
c[0] = a[0];
int max0 = c[0];
for(int i = 1; i <= n; i++) {
c[i] = c[i - 1] + a[i] - b[i];
if(c[i] > max0){
max0 = c[i];
}
}
for(int i = 1; i <= n; i++) {
int max = max0;
for(int j = n; j >= i; j--) {
if(c[j] + b[i] > max)
max = c[j] + b[i];
}
cout << max << " ";
}
return 0;
}
总结
前缀和,后缀和
前缀和和后缀和是两种常见的思想,用于快速计算数组(或序列)中某个范围内的和。它们通过预处理数据来减少后续操作的时间复杂度,常用于解决一些涉及区间求和的问题。
-
前缀和:
- 前缀和是一个累积和的概念,表示数组中每个元素及其前面所有元素的和。即
prefix[i] = arr[0] + arr[1] + ... + arr[i]
。 - 利用前缀和数组,给定一个区间
[l, r]
,区间和可以通过prefix[r] - prefix[l-1]
快速计算出来,从而减少了区间和计算的时间复杂度,从 O(n) 降低到 O(1)。
- 前缀和是一个累积和的概念,表示数组中每个元素及其前面所有元素的和。即
-
后缀和:
- 后缀和与前缀和相似,但它是从数组的末尾向前累积的。即
suffix[i] = arr[i] + arr[i+1] + ... + arr[n-1]
。 - 使用后缀和数组,类似地,给定区间
[l, r]
的后缀和也可以快速计算。
- 后缀和与前缀和相似,但它是从数组的末尾向前累积的。即
这两种思想主要用于优化范围查询问题,尤其是在需要频繁计算区间和的场景下,非常有效。
相同输入,多次运行结果不同的可能原因
1. 未定义行为 (Undefined Behavior)
- 越界访问数组:访问数组时,超出了数组的边界,导致程序读取未定义的内存区域,产生不稳定的结果。
- 空指针或悬空指针:在未初始化或已删除的指针上进行操作,会导致访问错误的内存位置。
- 数据竞争:在多线程程序中,如果多个线程同时访问和修改共享数据而没有适当的同步措施,可能会导致数据不一致。
- 使用未初始化的变量:未初始化的局部变量有时会包含垃圾值,导致程序行为不可预测。
2. 随机数
- 如果程序依赖于随机数生成(如
rand()
或std::random
),且未正确设置随机种子(如未调用srand()
或使用不稳定的种子),则每次运行时随机数序列可能不同,从而导致不同的结果。
3. 多线程或并发问题
- 线程调度不确定性:在并发程序中,线程的执行顺序由操作系统决定,如果没有正确的同步机制,多个线程同时访问共享资源时可能导致竞态条件(race conditions),从而导致结果不稳定。
- 死锁或活锁:多个线程相互等待对方释放资源,导致程序挂起或无限循环,可能在不同的执行中表现不同。
感想
这道题刚开始一直想不通,在网上看了别人的题解,也问了deepseek等等,还是不太理解,就先放一边了,隔了几天又去看才想明白,这次只花了一个小时完成思路整理和编程,还是比较满意的,写完以后也还是很有成就感的。
这道题难点在于思路,建议先当作数学题,推出求解过程,想清楚如何求解。