蓝桥杯专练:借教室问题

报名了acwing的蓝桥杯每日一题,在此总结今晚课上的内容,以便日后复习。
今晚的例题是借教室问题,题目如下:
在这里插入图片描述
在这里插入图片描述
题目虽然非常的长,但是仔细阅读我们就会发现他其实并不难。接下来我们做简单的分析:

  • 首先我们可以发现他给的数据非常大,如果我们用暴力做法一定是拿不到满分的,此时我们就要考虑使用算法来简便操作。
    在这里插入图片描述
    我们假设k为第一次无法满足条件的情况,把线段从中点分开,我们可以得出他有二段性

     这里我们补充一下什么是二段性:我们可以找到一个性质,使左边满足右边不满足,亦或者右边满足左边不满足。这样通过数次的循环,就可以分出答案的位置
    
int l= 0,r=m;
while(l<r)
{
	int mid = l+r+1>>1;//二分出来的中点
	//这里的‘>>1’表示除以2,相比‘/’,使用‘>>’更加快速
	if(check(mid)) l =mid;//满足条件,更新左边界
	else r =mid-1;//不满足条件,更新右边界
}

以上步骤为二分,值得注意的是,二分时需要注意边界问题,不然会造成time limit error,这里教大家一个简单的方法,即当if后是l=mid时,就让l+r再加上1;反之如果if后是r = mid,就值需要让mid= l+r>>1即可。

我们可以通过分析得知,这道题也是在一个数列的区间内加上或减去一个数,
根据我上一篇博客的例题不难发现,这里考察的是差分(关于差分的内容我就不再赘述,想详细了解的可以点击这里查看详细
我们接下来便重点了解check是如何实现的:

const int N = 100010;
typedef long long ll;
ll b[N];//差分数组
int n,m;//n表示天数,m表示订单的数量
int w[N],d[N],s[N],t[N];//w表示剩余教室的个数,d表示的是需要租借的个数
//s表示开始接的天数,t表示结束租借的天数
 bool check(int mid)
{
	memset(b,0,sizeof b);//将b的内容全部初始化为0
	for(int i =1;i<=mid;i++)
	{
		b[s[i]] += d[i];
		b[t[i]+1] -=d[i];//将s[i]到t[i]之间的值都加上d[i];
		
	}
	for(int i =1;i<=n;i++)
	{
		d[i]+=d[i-1];
		if(b[i]>w[i]) return false;
	}
	return true;
}

到此为止,这道题基本就已经做完了,接下来的就是补全步骤。
完整代码如下:

#include <iostream>
#include <cstring>

using namespace std;

typedef long long ll;

const int N = 1000010;

int n,m;//n表示天数,m表示订单的数量
int w[N],d[N],s[N],t[N];//w表示剩余教室的个数,d表示的是需要租借的个数
//s表示开始接的天数,t表示结束租借的天数

ll b[N];//差分数组

bool check(int mid)
{
    memset(b,0,sizeof b);//将差分数组初始化
    for(int i =1;i<=mid;i++)
    {
        b[s[i]]+=d[i];
        b[t[i]+1]-=d[i];

    }
    for(int i =1;i<=n;i++)
    {
        b[i]+=b[i-1];
        if(b[i]>w[i]) return false;
    }
    return true;
}

int main()
{
    cin>>n>>m;
    for(int i = 1;i<=n;i++) scanf("%d",&w[i]);
    for(int i =1;i<=m;i++) scanf("%d%d%d",&d[i],&s[i],&t[i]);
    int l=0,r=m;
    while(l<r)
    {
        int mid =l+r+1>>1;
        if(check(mid)) l = mid;
        else r=mid-1;
    }
    if(r==m) puts("0");
    else printf("-1\n%d\n",r+1);
    return 0;
}

有几点需要注意

  1. 当数据非常大的时候,尽量使用输入输出时尽量使用scanf和printf,比cin和cout快很多
  2. 我可以看到w和d的范围是非常大的,已经超出了int,所以我们这里就要用long long,我这里是为了方便,直接把long long设成ll
  3. 我在前缀和与差分里说过,使用这两个方法时要从1开始,但我这里为什么从0开始了呢,因为我们考虑到了一种非常极端的情况,那就是第一个订单的供应量就无法满足。
  4. memset的作用是快速初始化数组,头文件是

以上就是全部内容了,如果有什么不对的地方或者改进的方法还请告诉我,我都会听取建议。

  • 13
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值