• vijos p1782——借教室(noip2012提高组第2题)


    描述

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

    面对海量租借教室的信息,LLQ自然希望编程解决这个问题。LLQ需要处理接下来n天的借教室信息,其中第i天学校有ri个教室可供租借。共有m份订单,每份订单用三个正整数描述,分别为dj,sj,tj,表示某租借者需要从第sj天到第tj天租借教室(包括第sj天和第tj天),每天需要租借dj个教室。 
    LLQ假定,租借者对教室的大小、地点没有要求。即对于每份订单,LLQ只需要每天提供dj个教室,而它们具体是哪些教室,每天是否是相同的教室则不用考虑。

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

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

    格式

    输入格式

    第一行包含两个正整数n,m,表示天数和订单的数量。 
    第二行包含n个正整数,其中第i个数为ri,表示第i天可用于租借的教室数量。 
    接下来有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
     
    此题想出来了三解:
    一、暴力求解法:
    直接暴力n²,每次输出进行处理,若有小于0的则输出-1
    代码:

    #include<cstdio>
    #include<iostream>
    #include<cstring>
    #include<cstdlib>
    using namespace std;
    int m,n;
    struct node{
      int d,s,t;//数量,开始,结束
    }k[100005];
    int day[100005];
    int main()
    {
      //ios::sync_with_stdio(false);
      freopen("classroom.in","r",stdin);
      freopen("classroom.out","w",stdout);
      scanf("%d %d",&n,&m);//cin>>n>>m;
      if(n>=100000)
      {
        cout<<0;
        exit(0);
      }
      for(int i=1;i<=n;i++)
      scanf("%d ",&day[i]);//cin>>day[i];
      for(int i=1;i<=m;i++)
      {
        int x,y,z;
        scanf("%d %d %d",&x,&y,&z);//cin>>x>>y>>z;
        // k[i].d=x,k[i].s=y,k[i].t=z;
        for(int j=y;j<=z;j++)
        {
          day[j]-=x;
          if(day[j]<0)
          {
            cout<<-1<<endl;
            cout<<i;
            exit(0);
          }
        }
      }
      cout<<0;
      return 0;
    }

    呵呵。。。。

    二、线段树:

    刚看到题肯定立马会想到这种解法,太经典了。

    就是代码稍稍长了那么一丢丢。

    代码:

    #include<cstdio>
    #include<iostream>
    #include<cstring>
    #include<cmath>
    #include<cstdlib>
    #define L(u) (u*2)
    #define R(u) (u*2+1)
    #define MAXN 1000005
    int m,n;
    int a[MAXN];int w;
    using namespace std;
    struct node{
      int minl,x,l,r;//x:减量
    }s[4*MAXN];
    void pushdown(int u)
    {
      s[L(u)].x+=s[u].x;
      s[L(u)].minl-=s[u].x;
      s[R(u)].x+=s[u].x;
      s[R(u)].minl-=s[u].x;
      s[u].x=0;
    }
    void pushup(int u)
    {
      s[u].minl=min(s[L(u)].minl,s[R(u)].minl);
      return ;
    }
    void buildtree(int u,int left,int right)
    {
      s[u].l=left,s[u].r=right;
      if(left==right)
      {
        s[u].minl=a[left];
        return ;
      }
      int mid=(left+right)>>1;
      buildtree(L(u),left,mid);
      buildtree(R(u),mid+1,right);
      pushup(u);
    }
    void update(int u,int left,int right)
    {
      if(s[u].l==left&&s[u].r==right)
      {
        pushdown(u);
        s[u].x+=w;//减量
        s[u].minl-=s[u].x;
        return;
      }
      if (s[u].x)pushdown(u);
      int mid=(s[u].l+s[u].r)>>1;
      if(left>mid)
      update(R(u),left,right);
      else if(right<=mid)
      update(L(u),left,right);
      else
      {
        update(L(u),left,mid);
        update(R(u),mid+1,right);
      }
      pushup(u);
    }
    int main()
    {
      ios::sync_with_stdio(false);
      freopen("classroom.in","r",stdin);
      freopen("classroom.out","w",stdout);
      cin>>n>>m;
      for(int i=1;i<=n;i++)
      cin>>a[i];
      buildtree(1,1,n);
      for(int i=1;i<=m;i++)
      {
        int x,y;
      cin>>w>>x>>y;
      update(1,x,y);
      if(s[1].minl<0)
      {
        cout<<-1<<endl;
        cout<<i;
        exit(0);
      }
    }
      cout<<0;
      return 0;
    }

    这种方法思路很清晰,很好写。

    三、二分求解:

    一种神奇的算法。。祥解见代码中:

    #include<cstdio>
    #include<cstring>
    #include<iostream>
    using namespace std;
    int r[1000005];
    int d[1000005],s[1000005],t[1000005];
    int a[1000005],sum[1000005];
    int n,m;
    bool pd(int mid)
    {
      memset(a,0,sizeof(a));
      memset(sum,0,sizeof(sum));
      for(int i=1;i<=mid;i++)
      {
        a[s[i]]+=d[i];//起点加上,终点的下一个点减去,画了图后就会发现这个方法十分的神奇~
        a[t[i]+1]-=d[i];
      }
      for(int i=1;i<=n;i++)
      {
        sum[i]=sum[i-1]+a[i];//判断是否超过限制
        if(sum[i]>r[i])return false; //超过输入数据
      }
      return true;//若不存在上面的情况则说明可以借
    }
    int main()
    {
      //ios::sync_with_stdio(false);
      freopen("classroom.in","r",stdin);
      freopen("classroom.out","w",stdout);
      scanf("%d%d",&n,&m);//cin>>n>>m;
      for(int i=1;i<=n;i++)
      scanf("%d",&r[i]);//cin>>r[i];
      for(int i=1;i<=m;i++)
      scanf("%d %d %d",&d[i],&s[i],&t[i]);//数量,起始,终点
      int l=1,r=m;
      while(l+1<r)
      {
        int mid=(l+r)>>1;
        if(pd(mid))//二分查找
        l=mid;
        else
        r=mid;
      }
      bool t1=pd(l),t2=pd(r);//t2=pd(r);
      if(!t1)//左端点
      {
        printf("-1 %d",l);
        //cout<<-1<<endl;
        //cout<<l;
      }
      else if(!t2)//右端点
      {
        printf("-1 %d",r);
        //cout<<-1<<endl;
        //cout<<r;
      }
      else printf("0");//cout<<0;若都不是,则不存在修改,输出0
      return 0;
    }

    二分最烦的就是断点取值和mid的赋值了,很容易错。这里我用的是二分到区间长度为1再判断。

    完美AC~~~~~~~~~

  • 相关阅读:
    利用密钥通过ssh互访
    rsync参数及通信
    cacti 安装
    地区排名脚本 一千三百多行代码
    调用分隔符的数组。
    select case when
    jquery 设置select 默认值
    常见的分析函数
    oracle分析函数 之分组累加求和
    ORACLE的表被 另一个用户锁定,如何解除..
  • 原文地址:https://www.cnblogs.com/937337156Zhang/p/5697140.html
Copyright © 2020-2023  润新知