登陆

还是个水题QwQ
还是先来一句话题意:给定n个区间[l,r],每个区间在这整段时间内都要独自占用一份资源,求最少几份资源能够满足所有区间

还是先看数据,第一部分,我还是不知道怎么做,就当是给各路鬼畜做法准备的吧

解法一:差分+离散化

没有学过差分的同学请百度一篇博客学一学吧,差分还是个简单的东西的,当然不想学的可以直接跳解法二

可以想到,差分的套路,每次区间开头,在差分数组++,在区间结尾恰好后一位置,差分数组--。

那么我们按照序列坐标跑一遍差分数组,即s[i]+=s[i-1],再从中取出差分数组的最大值即可

大致模拟一下这个过程



所以最终复杂度就是线性的区间个数加上坐标范围

但是只能得70%分,因为100%的数据坐标很大啊

再加上离散化就可以AC了,这里就不再赘述离散化的具体实现了,大家可以自行百度

#include<iostream>
#include<cstdio>
#include<algorithm>
#define int long long
using namespace std;
struct node{
    int l,r;
}a[100010];
int n,ans,cnt,mp[200010],tot,s[200010];
signed main()
{
    scanf("%lld",&n);
    for(int i=1;i<=n;i++)
    {
        scanf("%lld%lld",&a[i].l,&a[i].r);
        mp[++cnt]=a[i].l,mp[++cnt]=a[i].r;
    }
    sort(mp+1,mp+cnt+1);
    cnt=unique(mp+1,mp+cnt+1)-mp-1;
    for(int i=1;i<=n;i++)
    {
        a[i].l=lower_bound(mp+1,mp+cnt+1,a[i].l)-mp;
        a[i].r=lower_bound(mp+1,mp+cnt+1,a[i].r)-mp;
        s[a[i].l]++,s[a[i].r+1]--;
    }
    for(int i=1;i<=cnt;i++)
    {
        s[i]+=s[i-1];
        ans=max(ans,s[i]);
    }
    printf("%lld
",ans);
    return 0;
}



解法二:贪心

按照开始进攻的时间把进攻排序

维护一个数组S,记录当前每个编队安排进去的最后一次打击,最初没有编队

依次对于每次进攻,扫描数组S,找到任意一个编队,满足当前的进攻开始时间不早于编队的最后一次打击的结束时间。如果这样的编队不存在,则新建一个编队。

这个贪心算法的时间复杂度是O(n^2),请读者自行证明其正确性。这样就得到了30%

我们可以用一个小根堆维护每个编队最后一次打击结束的时间,尝试把当前的进攻安排在堆顶(结束时间最早)的编队下。这样时间复杂度就可以降低到O(nlogn)

#include<iostream>
#include<cstdio>
#include<queue>
#include<algorithm>
#define int long long
using namespace std;
struct node{
    int l,r,id;
}a[1000010];
struct data{
    int id,t;
};
priority_queue<data>q;
int n,ans[1000010],cnt;
bool cmp(const node &x,const node &y)
{
    return x.l<y.l;
}
bool operator <(const data &x,const data &y)
{
    return x.t>y.t;
}
signed main()
{
    scanf("%lld",&n);
    for(int i=1;i<=n;i++)
        scanf("%lld%lld",&a[i].l,&a[i].r),a[i].id=i;
    sort(a+1,a+n+1,cmp);
    q.push((data){++cnt,a[1].r}),ans[a[1].id]=cnt;
    for(int i=2;i<=n;i++)
    {
        data u=q.top();
        q.pop();
        if(a[i].l>u.t)
            q.push((data){u.id,a[i].r}),ans[a[i].id]=u.id;
        else
            q.push((data){++cnt,a[i].r}),q.push(u),ans[a[i].id]=cnt;
    }
    printf("%lld
",cnt);
    return 0;
}

解法三:还是贪心,只是用set维护

来自@_23333神犇的想法

按最早进攻时间对进攻排序

对于每次进攻

把进攻结束时间之后(即可以下次打击的最早时间)扔进$set$

每次从set中lower_bound

如果这之前没有能够继续的编队,就新建一个

否则用那个继续这次进攻

#include<bits/stdc++.h>
#define N 100005
#define l first
#define r second
#define pr pair<int,int>
#define int long long
using namespace std;

inline void rd(int &X)
{
    X=0;int w=0;char ch=0;
    while(!isdigit(ch))w|=ch=='-',ch=getchar();
    while( isdigit(ch))X=(X<<3)+(X<<1)+(ch^48),ch=getchar();
    X=w?-X:X;
}

int n;
multiset<int> s;
multiset<int> :: iterator it;
pr a[N];

signed main()
{
    rd(n);
    for(int i=1;i<=n;i++)
        rd(a[i].l),rd(a[i].r);
    sort(a+1,a+1+n);
    s.insert(a[1].r+1);
    for(int i=2;i<=n;i++)
    {
        it=s.upper_bound(a[i].l);
        if(it==s.begin())
            s.insert(a[i].r+1);
        else {
            it--;
            s.erase(it);
            s.insert(a[i].r+1);
        }
    }
    cout<<s.size();
}
原文地址:https://www.cnblogs.com/ivanovcraft/p/9664454.html