题解 | #[NOIP2012]借教室#

[NOIP2012]借教室

https://ac.nowcoder.com/acm/problem/16564

题目描述

在大学期间,经常需要租借教室。大到院系举办活动,小到学习小组自习讨论,都需要向学校申请借教室。教室的大小功能不同,借教室人的身份不同,借教室的手续也不一样。

面对海量租借教室的信息,我们自然希望编程解决这个问题。

我们需要处理接下来n天的借教室信息,其中第i天学校有ri个教室可供租借。共有m份订单,每份订单用三个正整数描述,分别为dj, sj, tj,表示某租借者需要从第sj天到第tj天租借教室(包括第sj天和第tj天),每天需要租借dj个教室。

我们假定,租借者对教室的大小、地点没有要求。即对于每份订单,我们只需要每天提供dj个教室,而它们具体是哪些教室,每天是否是相同的教室则不用考虑。

借教室的原则是先到先得,也就是说我们要按照订单的先后顺序依次为每份订单分配教室。如果在分配的过程中遇到一份订单无法完全满足,则需要停止教室的分配,通知当前申请人修改订单。这里的无法满足指从第sj天到第tj天中有至少一天剩余的教室数量不足dj个。

现在我们需要知道,是否会有订单无法完全满足。如果有,需要通知哪一个申请人修改订单。

alt

题目分析

最容易想到的想法,根据订单依次将每天需要的教室数量求出来,当某个订单需要的教室数量已经不能由可用的教室数量满足,该订单就是需要修改的方法。

于是有暴力解。


import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.*;
public class Main
{
    public static void main(String []args) throws IOException {
        BufferedReader bf = new BufferedReader(new InputStreamReader(System.in));
        String temp = bf.readLine();
        String []str=temp.split(" ");
        int n=Integer.parseInt(str[0]);
        int m=Integer.parseInt(str[1]);
        int []day=new int[n+2];
        temp=bf.readLine();
        str=temp.split(" ");
        for(int i=1;i<=n;i++)
        {
            day[i]=Integer.parseInt(str[i-1]);
        }
        int res=0;
        int []needroom=new int[n+1];
        int d[]=new int [m+1],s[]=new int [m+1],t[]=new int [m+1];
        for(int i=1;i<=m;i++)
        {
            temp=bf.readLine();
            str=temp.split(" ");
            d[i]=Integer.parseInt(str[0]);
            s[i]=Integer.parseInt(str[1]);
            t[i]=Integer.parseInt(str[2]);

            for(int j=s[i];j<=t[i];j++)
            {
                needroom[j]+=d[i];
                if(needroom[j]>day[j])
                {
                    res=i;
                    break;
                }
            }
            if(res!=0)
                break;
        }
        int left=1;
        int right=m;
        if(res==0)
            System.out.println(res);
        else {
            System.out.println(-1);
            System.out.println(res);
        }
    }
}

运行结果

alt


暴力解超时,进一步分析问题,由于我们需要频繁的修改区间信息(订单中的l,r),因此可以使用差分数组来保存(0,m)个订单的修改操作,然后还原原来的最后判断弄一下是否有订单小于0。


差分数组

简单描述一下差分数组

使用cha[i]=arr[i]-arr[i-1] 来记录arr[i] 和 arr[i-1] 的差,z之后当需要查找 arr[i] 需要从arr[0] 一路还原回来,这样做的好处是,当我们有任何的区间修改操作时,只需要在差分数组上 cha[left] 和 cha[right+1] 进行修改,而不需要遍历整个arr[left...right], 坏处是 需要查找某一个arr[i]时,需要一步步还原回来,所以

该方法适用于区间频繁修改,而且这个区间范围是比较大的,正如本题,m,s,t数值范围很大。


对于所有订单[1,m],如果订单[1,j]能够使得某一天的教室数量不够,那么[1,m]一定能够使得某一天的教室数量不够,找到第一个出现这种情况的 j(某天教室数量不够),使用二分查找

代码:

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.*;
public class Main
{
    //r[i] 表示第i天可用于租借的教室数量
    //d[i] 表示第i个订单租借的数量
    //s[i] 表示第i个订单的开始时间
    //t[i] 表示第i个订单的结束时间
    public static boolean check(int mid,int n,int m,int []r,int []d,int []s,int []t) {
        int[] cha = new int[n + 2];//差分数组
        int[] r1 = new int[n + 1];
        for (int i = 1; i <= n; i++)
            r1[i] = r[i];
        for (int i = 1; i <= mid; i++)
        {
            cha[s[i]]-=d[i];
            cha[t[i]+1]+=d[i];
        }
        for(int i=1;i<=n;i++)
        {
            cha[i]+=cha[i-1];
            r1[i]+=cha[i];
            if(r1[i]<0)
                return false;
        }
        return true;
    }


    public static void main(String []args) throws IOException {
        BufferedReader bf = new BufferedReader(new InputStreamReader(System.in));
        String temp = bf.readLine();
        String []str=temp.split(" ");
        int n=Integer.parseInt(str[0]);
        int m=Integer.parseInt(str[1]);
        int []day=new int[n+2];
        temp=bf.readLine();
        str=temp.split(" ");
        for(int i=1;i<=n;i++)
        {
            day[i]=Integer.parseInt(str[i-1]);
        }
        int res=0;
        int d[]=new int [m+1],s[]=new int [m+1],t[]=new int [m+1];
        for(int i=1;i<=m;i++)
        {
            temp=bf.readLine();
            str=temp.split(" ");
            d[i]=Integer.parseInt(str[0]);
            s[i]=Integer.parseInt(str[1]);
            t[i]=Integer.parseInt(str[2]);
        }
        int left=1;
        int right=m;
        while(left<=right)
        {
            int mid=(left+right)>>>1;
            if(check(mid,n,m,day,d,s,t))
                left=mid+1;
            else
                right=mid-1;

        }

        if(left-1==m)
            System.out.println(0);
        else {
            System.out.println(-1);
            System.out.println(left);
        }
    }
}
全部评论

相关推荐

头像
10-14 23:01
已编辑
中国地质大学(武汉) Java
CUG芝士圈:虽然是网上的项目,但最好还是包装一下,然后现在大部分公司都在忙校招,十月底、十一月初会好找一些。最后,boss才沟通100家,别焦虑,我去年暑假找第一段实习的时候沟通了500➕才有面试,校友加油
点赞 评论 收藏
分享
joe2333:怀念以前大家拿华为当保底的日子
点赞 评论 收藏
分享
评论
点赞
收藏
分享
牛客网
牛客企业服务