用前缀和数组解决泡澡问题

一、前缀和的原理和特点(prefix)

        1、对于一个数组a[](下标从1开始),我们定义一个前缀和数组prefix[],满足:                                prefix[i] = \sum_{j=1}^{i}a[j],即1-i的数组元素的和。

       2、prefix[]有一个重要的特性,可以快速用于生成prefix[]:

                prefix[i]=\sum_{j=1}^{i-1}a[j] + a[i] = prefix[i-1] + a[i]

        3、prefix[]可以O(1)的求数组a[]的一段区间的和:

                sum(l,r) = prefix[r] - prefix[l-1]

        (鼠标画的好烂妈呀.....)

                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 的情况。

  • 9
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值