2021牛客暑期多校训练营2 G.League of Legends(转化+单调队列)

G.League of Legends

Zechariah_2001题解
对于可以包含其他区间的大区间,要使得答案最优无非就是两种分组方式:单独一组或者与被包含的区间一组。单独一组那么贡献就是区间长度;如果说与被包含的区间一组,对答案贡献为0,因为根据题意,如果有多个区间,添加区间实际上是添加限制,不会是当前答案变优。

把上面的覆盖别的区间的区间出去后,剩下的区间按照左端点升序排序后不难发现右端点同样也会升序排列。

设计dp:

状态表示: f j , i f_{j,i} fj,i考虑前 i i i个区间,分成 j j j组的最大值
状态转移: f j , i = max ⁡ { f j − 1 , k − 1 + R k − L i } , R k > L i f_{j,i}=\max\{f_{j-1,k-1}+\text R_k-\text L_i\},\text R_k>\text L_i fj,i=max{fj1,k1+RkLi},Rk>Li

R k > L i \text R_k>\text L_i Rk>Li这个限制保证每组至少玩1分钟游戏

如果没有上述现之,可以搞一个前缀的 max ⁡ { f j − 1 , k − 1 + R k } \max\{f_{j-1,k-1}+\text R_k\} max{fj1,k1+Rk}优化转移,加上此限制后需要单调队列优化转移。

Code1 暴力

895ms 竟然也能过???

#include<bits/stdc++.h>
using namespace std;
using ll=long long;
using pii=pair<int,int>;
using pli=pair<ll,int>;
constexpr ll mod=1e9+7;
//=============================
int rd()
{
    int res=0;
    char ch=getchar();
    while(!isdigit(ch)) ch=getchar();
    while( isdigit(ch)) res=(res<<1)+(res<<3)+(ch^48),ch=getchar();
    return res;
}
const int N=5010;
int n,m;
struct node
{
    int l,r;
    bool operator<(const node&o)const
    {
        return l<o.l||l==o.l&&r>o.r;
    }
}a[N],b[N];
int seg[N],cnt,tot;
int f[N][N];// f[j][i] 前i个分成j组
int main()
{
    n=rd(),m=rd();
    for(int i=1;i<=n;i++) b[i].l=rd(),b[i].r=rd();
    sort(b+1,b+1+n);
    
    int maxr=0x3f3f3f3f;
    for(int i=n;i;i--)
    {
        if(b[i].r>=maxr) //能够覆盖别的线段
            seg[++tot]=b[i].r-b[i].l;
        else
            maxr=b[i].r,a[++cnt]=b[i];
    }
    sort(a+1,a+1+cnt);
    memset(f,-0x3f,sizeof f);
    f[0][0]=0;
    
    for(int j=1;j<=cnt;j++)
        for(int i=j;i<=cnt;i++)
            for(int k=1;k<=i;k++)
                if(a[k].r>a[i].l)
                    f[j][i]=max(f[j][i],f[j-1][k-1]+a[k].r-a[i].l);
                
    sort(seg+1,seg+1+tot,greater<int>());
    int ans=0,sum=0;
    for(int i=0;i<=min(m,tot);i++)
    {
        sum+=seg[i];
        if(f[m-i][cnt]!=-1) ans=max(ans,f[m-i][cnt]+sum);
    }
    cout<<ans<<'\n';
    return 0;
}
Code2 单调队列

83ms

#include<bits/stdc++.h>
using namespace std;
using ll=long long;
using pii=pair<int,int>;
using pli=pair<ll,int>;
constexpr ll mod=1e9+7;
//=============================
int rd()
{
    int res=0;
    char ch=getchar();
    while(!isdigit(ch)) ch=getchar();
    while( isdigit(ch)) res=(res<<1)+(res<<3)+(ch^48),ch=getchar();
    return res;
}
const int N=5010;
int n,m;
struct node
{
    int l,r;
    bool operator<(const node&o)const
    {
        return l<o.l||l==o.l&&r>o.r;
    }
}a[N],b[N];
int seg[N],cnt,tot;
int f[N][N];// f[j][i] 前i个分成j组
int q[N];
int main()
{
    n=rd(),m=rd();
    for(int i=1;i<=n;i++) b[i].l=rd(),b[i].r=rd();
    sort(b+1,b+1+n);
    
    int maxr=0x3f3f3f3f;
    for(int i=n;i;i--)
    {
        if(b[i].r>=maxr) //能够覆盖别的线段
            seg[++tot]=b[i].r-b[i].l;
        else
            maxr=b[i].r,a[++cnt]=b[i];
    }
    sort(a+1,a+1+cnt);
    memset(f,-0x3f,sizeof f);
    f[0][0]=0;
    // f[j-1][k-1] + a[k].r    a[k].r>a[i].l && k<=i

    for(int j=1;j<=cnt;j++)
    {
        int tt=-1,hh=0;
        for(int i=1;i<j;i++)
        {
            while(hh<=tt)
            {
                int k=q[tt];
                if(f[j-1][i-1]+a[i].r>=f[j-1][k-1]+a[k].r) --tt;
                else break;
            }
            q[++tt]=i;
        }
        for(int i=j;i<=cnt;i++)
        {
            while(hh<=tt) 
            {
                int k=q[hh];
                if(a[k].r<=a[i].l) hh++;
                else break;
            }
            while(hh<=tt)
            {
                int k=q[tt];
                if(f[j-1][i-1]+a[i].r>=f[j-1][k-1]+a[k].r) --tt;
                else break;
            }
            q[++tt]=i;
            int k=q[hh];
            f[j][i]=max(f[j][i],f[j-1][k-1]+a[k].r-a[i].l);
        }
    }
    sort(seg+1,seg+1+tot,greater<int>());
    int ans=0,sum=0;
    for(int i=0;i<=min(m,tot);i++)
    {
        sum+=seg[i];
        if(f[m-i][cnt]!=-1) ans=max(ans,f[m-i][cnt]+sum);
    }
    cout<<ans<<'\n';
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值