一、前缀和的原理和特点(prefix)
1、对于一个数组a[](下标从1开始),我们定义一个前缀和数组prefix[],满足: ,即1-i的数组元素的和。
2、prefix[]有一个重要的特性,可以快速用于生成prefix[]:
3、prefix[]可以O(1)的求数组a[]的一段区间的和:
(鼠标画的好烂妈呀.....)
prefix[i]=a[1]
4、prefix[]是一种预处理的算法,只适用于a数组为静态数组的情况,即a数组中的元素在区间和查询过程中不会进行更改
若要实现“先区间修改,在区间查询”,可以使用差分数组;
如果要"一边修改,一边查询",需要使用树状数组或线段数等数据结构。
二、蓝桥题库泡澡
我的思路(没有考虑到运行时间)
因为那个时候也没有想到用前缀和数组。
#include<bits/stdc++.h>
using namespace std;
const int N = 1e6 + 3;
using ll = long long;
ll d[N];
int main()
{
ll n, w; cin >> n >> w;
for (int i = 1; i <= 2*1e5 ;i ++) d[i] = w;
ll Max = 0;//记录最大的洗澡时间点 待会遍历
for (int i = 1; i <= n; i++)
{
ll s, t ,p; cin >> s >> t >> p;
for (ll j = s; j <= t;j++) d[j] -= p;
Max = max(Max,t);
}
int flag = 1;
for (ll i = 1; i <= Max; i++)
{
if (d[i] < 0)
{
cout << "No" << '\n';
flag = 0;
break;
}
}
if (flag)
cout << "Yes" << '\n';
}
可以运行,但是不建议。因为我遍历到最大数组空间,把它们全部赋值为w,但是实际上泡澡时间不会到那么久,也就是说我这个数组的后面都是冗余的。
目前这段代码在判断是否存在负数的时候,使用了一个暴力的遍历方式。这个操作的时间复杂度是O(N),其中N是最大的洗澡时间点。
使用前缀和优化暴力枚举
可以考虑使用前缀和优化这个操作。具体来说,可以在输入数据的时候,对每个时间点的浴室使用量进行累加,得到一个前缀和数组。然后在判断的时候,只需要对前缀和数组进行遍历,时间复杂度为O(1)。
#include<bits/stdc++.h>
using namespace std;
const int N = 2e5 + 3;
using ll = long long;
ll prefix[N]; //前缀和数组
//利用前缀和的好处
//1、不用初始化原来的数组
//2、降低循环次数,将时间复杂度降低为O(N)
int main()
{
ll n, w; cin >> n >> w;
ll Max = 0;//记录最大的洗澡时间点 待会遍历
for (int i = 1; i <= n; i++)
{
ll s, t ,p; cin >> s >> t >> p;
prefix[s] += p;//更新前缀和数组
prefix[t] -= p;
Max = max(Max,t);
}
int flag = 1;
for (ll i = 1; i <= Max; i++) prefix[i] += prefix[i-1];
for (ll i = 0; i <= Max; i++)
{
if (prefix[i] > w)
{
cout << "No" << '\n';
flag = 0;
break;
}
}
if (flag)
cout << "Yes" << '\n';
}
注意这段代码中,最后一个循环是可以取到0的,原因如下:
前缀和数组 prefix
的下标从 0 开始,用来表示时间点 0 的情况。