• NOIP2012 洛谷P1083 借教室


    传送门

    题意:有一些学(xian)生(quan)要借教室。在n天内,第i天学校有ri个教室。有m份订单,每份订单有三个数值dj,sj,tj,分别表示这个订单从第sj天开始到第tj天结束(包括端点),每天需要dj个教室。

    我们要按照订单的顺序一次处理每一个订单,如果有某个订单不能满足(当天的教室数量小于需求数),就需要输出-1和这个订单的序号,否则输出0


    首先看到这个题,第一想到的当然是暴力了qwq。依次处理每一个订单,暴力从每一天的教室数量内减去需求数,如果某一天教室数量小于0,就输出这份订单的编号

    时间复杂度O(mn)

    代码:

    #include<bits/stdc++.h>
    using namespace std;
    
    inline int read()
    {
        int ans=0;
        char last=' ',ch=getchar();
        while(ch<'0'||ch>'9') last=ch,ch=getchar();
        while(ch>='0'&&ch<='9') ans=ans*10+ch-'0',ch=getchar();
        if(last=='-') ans=-ans;
        return ans;
    }
    
    struct ding
    {
        int d,s,t;
    }dan[1000005];
    
    int n,m;
    int r[1000005];
    
    int main()
    {
        n=read(),m=read();
        for(int i=1;i<=n;i++) r[i]=read();
        for(int i=1;i<=m;i++)
        {
            dan[i].d=read();
            dan[i].s=read();
            dan[i].t=read();
        }
        for(int i=1;i<=m;i++)
        {
            for(int j=dan[i].s;j<=dan[i].t;j++)
            {
                r[j]-=dan[i].d;
                if(r[j]<0)
                {
                    printf("-1
    %d",i);
                    return 0;
                }
                    
            }
        }
        printf("0");
    }

    能拿45分。


    开动你聪明的小脑袋想(tou)想(li),其实可以把每一个订单看成一个区间,那这个题不就是区间修改问题吗?于是我们很容易想到既好用又好写的线段树了。线段树操作方便,复杂度低,很适合做这道题

    代码:

    #include<cstdio>
    #include<iostream>
    #include<cstdlib>
    #include<iomanip>
    #include<cmath>
    #include<cstring>
    #include<string>
    #include<algorithm>
    #include<time.h>
    #include<queue>
    using namespace std;
    typedef long long ll;
    typedef long double ld;
    typedef pair<int,int> pr;
    const double pi=acos(-1);
    #define rep(i,a,n) for(int i=a;i<=n;i++)
    #define per(i,n,a) for(int i=n;i>=a;i--)
    #define Rep(i,u) for(int i=head[u];i;i=Next[i])
    #define clr(a) memset(a,0,sizeof a)
    #define pb push_back
    #define mp make_pair
    #define fi first
    #define sc second
    ld eps=1e-9;
    ll pp=1000000007;
    ll mo(ll a,ll pp){if(a>=0 && a<pp)return a;a%=pp;if(a<0)a+=pp;return a;}
    ll powmod(ll a,ll b,ll pp){ll ans=1;for(;b;b>>=1,a=mo(a*a,pp))if(b&1)ans=mo(ans*a,pp);return ans;}
    ll read(){
        ll ans=0;
        char last=' ',ch=getchar();
        while(ch<'0' || ch>'9')last=ch,ch=getchar();
        while(ch>='0' && ch<='9')ans=ans*10+ch-'0',ch=getchar();
        if(last=='-')ans=-ans;
        return ans;
    }
    //head
    
    哈哈哈没有想到吧这篇题解不是线段树
    线段树什么的根本不存在的QωQ

    咳咳咳。。。下面转入正题

    我们可以用差分数组鸭。

    对于一次从s开始到t,需要d的操作,我们只需要把差分数组的第s项-d,第t+1项+d就可以了。值得一提的是,负数显然不太好,所以我们可以把负数转化成正数然后比较大小

    把差分数组的第s项+d,第t+1项-d

    然后,对于这个神奇的答案范围,显然直接搞事情是不行的,我们可以二分,然后检查是否满足条件就可以了。

    上海世界外国语中学初三某C姓同学的题解(正解)

    代码:

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #define maxn 1000005
    
    using namespace std;
    
    int LEFT,MID,RIGHT,n,m,r[maxn],d[maxn],s[maxn],t[maxn],day[maxn];
    
    bool judge(int mid)
    {
        memset(day,0,sizeof(day));
        for (int i=1;i<=mid;i++)
        {
            day[s[i]]+=d[i];
            day[t[i]+1]-=d[i];
        }
        if (day[1]>r[1])
            return 1;
        for (int i=2;i<=n;i++)
        {
            day[i]+=day[i-1];
            if (day[i]>r[i])
                return 1;
        }
        //cout << -1 << endl;
        return 0;
    }
    
    int main()
    {
        //freopen("classroom.in","r",stdin);
        //freopen("classroom.ans","w",stdout);
        cin >> n >> m;
        for (int i=1;i<=n;i++)
            scanf("%d",&r[i]);
        for (int i=1;i<=m;i++)
            scanf("%d%d%d",&d[i],&s[i],&t[i]);
        LEFT=1;RIGHT=m;
        while (LEFT<RIGHT)
        {
            MID=(LEFT+RIGHT)/2;
            //cout << LEFT << " " << RIGHT << " " << MID << endl;
            if (judge(MID))
                RIGHT=MID;
            else LEFT=MID+1;
        }    
        if (m!=RIGHT) 
        {
            cout << -1 << endl;
            cout << RIGHT << endl;
        }
        else cout << 0 << endl;
        return 0;
    }

    接下来是神(wai)奇(men)解(xie)法(dao)

    这里我用了差分+前缀和的思想,把每一天需要的教室数量搞了出来,然后枚举每一天,直到发现供不应求的情况就滚回去一个个减,直到满足情况为止

    代码:

    #include<bits/stdc++.h>
    using namespace std;
    
    inline int read()
    {
        int ans=0;
        char last=' ',ch=getchar();
        while(ch<'0'||ch>'9') last=ch,ch=getchar();
        while(ch>='0'&&ch<='9') ans=ans*10+ch-'0',ch=getchar();
        if(last=='-') ans=-ans;
        return ans;
    }
    
    typedef long long ll;
    
    struct ding
    {
        int d,s,t;
    }dan[1000005];
    
    int n,m;
    int r[1000005];
    int cf[1000005],sum[1000005];
    int anss=2147483647;
    
    int main()
    {
        n=read(),m=read();
        for(int i=1;i<=n;i++) 
        {
            r[i]=read();
        }
        for(int i=1;i<=m;i++)
        {
            dan[i].d=read();
            dan[i].s=read();
            dan[i].t=read();
            sum[dan[i].s]+=dan[i].d;
            //这里用了比较大小的方法,因为据说负数容易re 
            sum[dan[i].t+1]-=dan[i].d;
        }
        for(int i=1;i<=n;i++) 
        {
            sum[i]+=sum[i-1];
        }//计算每一天的教室需求量 
        for(int i=1;i<=n;i++)
        {
            if(sum[i]>r[i])//枚举到需求量大于拥有量 
            {
                ll ans=sum[i]; 
                int j;
                for(j=m;j>=1;j--)
                {
                    if(dan[j].s<=i&&dan[j].t>=i)
                    {
                        ans-=dan[j].d;
                        //暴力一个个减回去直到符合条件
                        if(ans<=r[i]) break;
                    }
                }
                anss=min(anss,j);//取最前面的 
                if(anss==1) break;
            }
            
        }
        if(anss==2147483647) printf("0");
        else printf("-1
    %d",anss);
        return 0;
        //最坏复杂度为O(mn)
    }

    理论上最坏复杂度是O(mn)的。。。但是实际跑起来还贼快,一定是随机数据的锅qwq

  • 相关阅读:
    用户自定义控件
    sql 动态行转列
    sql 将表B中不存在表A的数据 插入到表A中
    C#获取键盘和鼠标操作的时间的类
    滚动条加粗和panel,gridControl结合用
    第三项任务——用例建模
    第二项任务——项目需求分析
    第一项任务:团队组建及项目启动
    实验报告(结对项目)
    实验报告(个人项目)
  • 原文地址:https://www.cnblogs.com/lcezych/p/11143268.html
Copyright © 2020-2023  润新知