借教室-洛谷-P1083

原题
在大学期间,经常需要租借教室。大到院系举办活动,小到学习小组自习讨论,都需要向学校申请借教室。教室的大小功能不同,借教室人的身份不同,借教室的手续也不一样。
面对海量租借教室的信息,我们自然希望编程解决这个问题。
我们需要处理接下来n天的借教室信息,其中第i天学校有ri个教室可供租借。共有m份订单,每份订单用三个正整数描述,分别为dj,sj,ti,表示某租借者需要从第sj天到第tj天租借教室(包括第sj天和第tj天),每天需要租借djdj个教室。我们假定,租借者对教室的大小、地点没有要求。即对于每份订单,我们只需要每天提供djdj
​个教室,而它们具体是哪些教室,每天是否是相同的教室则不用考虑。借教室的原则是先到先得,也就是说我们要按照订单的先后顺序依次为每份订单分配教室。如果在分配的过程中遇到一份订单无法完全满足,则需要停止教室的分配,通知当前申请人修改订单。这里的无法满足指从第sj 天到第tj天中有至少一天剩余的教室数量不足dj个。现在我们需要知道,是否会有订单无法完全满足。如果有,需要通知哪一个申请人修改订单。
输入输出格式
输入格式:
第一行包含两个正整数n,m,表示天数和订单的数量。
第二行包含n个正整数,其中第i个数为ri,表示第ii天可用于租借的教室数量。
接下来有m行,每行包含三个正整数dj,sj,tj,表示租借的数量,租借开始、结束分别在第几天。
每行相邻的两个数之间均用一个空格隔开。天数与订单均用从1开始的整数编号。
输出格式:
如果所有订单均可满足,则输出只有一行,包含一个整数 0。否则(订单无法完全满足)
输出两行,第一行输出一个负整数-1,第二行输出需要修改订单的申请人编号。
输入输出样例
输入样例#1:
4 3
2 5 4 3
2 1 3
3 2 4
4 2 4
输出样例#1:
-1
2
说明
【输入输出样例说明】
第1份订单满足后,4天剩余的教室数分别为 0,3,2,3。第 2份订单要求第 2 天到第 4天每天提供 3 个教室,而第 3 天剩余的教室数为 2,因此无法满足。分配停止,通知第2 个申请人修改订单。
【数据范围】
对于 100%的数据,有1 ≤ n,m ≤ 10^6,0 ≤ r_i,d_j≤ 10^9,1 ≤ s_j≤ t_j≤ n1≤n,m≤10
题意:
有n间教室,有m个任务,每一个让任务包含了该任务的需要借的教室数量,以及借教室的起始时间和结束时间。
问能不能够满足所有任务,若是能则输出0,若是不能则输出-1,和不能够满足的任务编号。
题解:
这道题目,开始以为是一道模拟水题,暴力求解了结果只得了40分,TLE了很多点。
之后看了标程后才发现这道题目,也可以用二分来做。
下面讲讲二分的具体做法:
这道题目要求的是不能满足的任务编号,所以我们可以二分求解最小不能满足的任务编号。判断函数中用到了差分数组diff【】,其实就是前缀和的逆数组(其实在这道题目里和普通数组一样)。每一个diff【i】存放的是第i天所需要的教室总数。之后再统一判断,(这里就是这道题降低时间复杂度的地方,他不需要对于每一个任务都判断是否符合,而是统一求解,判断出不能满足任务编号的区间)。若是所需教室数量大于教室总数,则证明再left-mid之间有不能够满足的编任务,反之则证明不能满足的任务在mid-right之间。(注解:开始可以提前判断m个任务是否都能满足,若是能够满足最直接输出0,若是不能则证明有不能够满足的任务,再进入二分循环)。
附上AC代码:

#include <iostream>
#include <cstring>
using namespace std;
int n,m,r[1000005],need[1000005],ans=2000000,diff[1000005];
struct task
{
    int d,st,ed;
}t[1000005];//任务数组,d代表借用时间,st起始时间,ed结束时间
int check(int x)
{
    memset(diff,0,sizeof(diff));//差分数组
    for(int i=1;i<=x;i++)
    {
        diff[t[i].st]+=t[i].d;//该任务的起始时间需要d间教室
        diff[t[i].ed+1]-=t[i].d;//该任务结束了就需要释放掉d间教室
    }
    for(int i=1;i<=n;i++)
    {
        need[i]=need[i-1]+diff[i];//记录n天中每一天需要的教室数量
        if(need[i]>r[i])return 0;//如果当天需要教室数量大于当天拥有的教室数量则证明在left-mid之间存在不能够满足的任务
    }
    return 1;//反之,则证明在left-mid之间的任务都能够满足
}
int main()
{
    cin>>n>>m;
    for(int i=1;i<=n;i++)
    {
        cin>>r[i];
    }
    for(int i=1;i<=m;i++)
    {
        cin>>t[i].d>>t[i].st>>t[i].ed;
    }
    int left=1,mid,right=m;//初始化,因为所有可能出现的不能完成任务编号只能在1-m之间。
    if(check(m)){cout<<0<<endl;return 0;}//判断是否都能满足
    while(left<right)
    {
        mid=(right+left)/2;
        if(check(mid))
            left=mid+1;//返回值是1代表在left-mid之间的任务都能满足
        else
            right=mid;//返回值是0代表在left-mid之间有不能满足的任务
    }
    cout<<-1<<endl;
    cout<<left<<endl;
    return 0;
}

欢迎评论!

猜你喜欢

转载自blog.csdn.net/wjl_zyl_1314/article/details/82890911