1083 借教室
这道题如果暴力的话肯定会超时,只能得45分
这道题的思路就是说每次这s-t天每一天都得减去借的桌子s,如果发现减去之后是一个负数说明这条订单不合理需要输出编号,那么我们暴力枚举的其实就是这个[s,t]区间,枚举的过程多大一千万,这个时间复杂度太高了,只能有45分
所以我们得进行一个优化,优化在哪里?肯定就是[s,t]区间得进行一个减少时间,用什么呢?
前缀和
前缀和其实是一个很简单的,其实表示方法就是s[i]=a[i]+a[i-1]+a[i-2]+a[i-3]+…+1,求出来前缀和数组就好了,那么我们的区间和然后区间和修改就好求了,O(1)复杂度,肯定就能通过了
但是…难就难在怎么求前缀和数组
我自己用函数来做了做,还是错了,所以,我得去看题解了
首先我们知道这道题是二分加上前缀和来做的
这道题其实用线段树很简单,直接干就行,但是我不想练线段树,直接用二分来练
所以我们这里使用差分数组,差分数组还得 配合着前缀和思想
我们有一组树,并且有一堆询问-----给定区间 l,r求l,r之间所有数的和
这样的话,暴力是肯定不行呢,那么前缀和怎么做?
用sum[i]存储前i个数的和,然后用sum[r]-sum[l-1]来表示l~r之间所有数的和,那么可以使用函数递归来实现
那么差分数组又是什么呢?只是前缀和的数组的逆运算
我们给定前i个数相邻两个数的差,求每一项a[i]
这就是用前一个值加上这个差就能求出来,比如diff[i]来记录a[i]-a[i-1]那么diff[i]+a[i-1]=a[i]了
---------------重新----------------
教室的大小功能不同,借教室人的身份不同,借教室的手续也不一样
做题,一定要弄懂题意,算法,数据
数据非常重要
我们只需要每天提供 dj个教室,而它们具体是哪些教室,每天是否是相同的教室则不用考虑,也就是说这个教室都是一样的,没有区别
至于这个订单的修改,也就是吧不满足,那么需要重新修改,如果有,需要通知哪一个申请人修改订单。这就是我们需要求的
题意我们都能理解
但是这个题的算法在哪里呢?
二分,前缀和,差分
首先:我们用暴力肯定是可以解决的,但不是正解
差分 差分是前缀和的逆运算,也就是一种dp思想
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<string>
using namespace std;
int n,m;
int diff[1000011],need[1000011],rest[1000011],r[1000011],l[1000011],d[1000011];
bool isok(int x)//这里就是一个判断
{
memset(diff,0,sizeof(diff));
for(int i=1;i<=x;i++)
{
diff[l[i]]+=d[i];
/*
diff[i]=diff[i]-diff[i-1]
diff[l[i]]=diff[l[i]]-diff[l[i-1]]
*/
diff[r[i]+1]-=d[i]; //用差分数组来搞定这个差
//接下来只需要判断差分数组就好了
}
for(int i=1;i<=n;i++)
{
need[i]=need[i-1]+diff[i];//计算相邻的
if(need[i]>rest[i])return 0;//教室不够
}
return 1;
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)scanf("%d",&rest[i]);//表示一个教室
for(int i=1;i<=m;i++)scanf("%d%d%d",&d[i],&l[i],&r[i]);//输入租借信息
int begin=1,end=m;
if(isok(m)){cout<<"0";return 0;}//全部满足租界
while(begin<end)
{
int mid=(begin+end)/2;
if(isok(mid))begin=mid+1;//二分查找,查找的是什么
else end=mid;
}
cout<<"-1"<<endl<<begin;//寻找的是没有的个数
}