【Codeforces436D】Pudding Monsters

题意:

  • 开始有无限长的一段格子,有 n 个格子种有布丁怪兽,一开始连续的布丁怪兽算一个布丁怪兽。
  • 每回合你可以将一个布丁怪兽向左或右移动,他会在碰到第一个布丁怪兽时停下,并与其合并。
  • 如果最左边的怪兽向左,你可以认为是将其移动到了无穷远处。
  • m个特殊格子,询问最终你最多可以让几个特殊的格子上被布丁覆盖。
  • n10000,m2000

题解:

  • 首先考虑两种状态: f[i] 表示前 i 个布丁(相同的布丁怪兽算长度个)最多覆盖的特殊格子数,g[i]表示前 i 个布丁,第i个不动的情况下最多覆盖的特殊格子数, sum(l,r) 表示 [l,r] 区间中有多少个特殊格子。那么答案就是 f[n]
  • 考虑如何转移:
  • 第一种:
  • 设当前转移坐标为 r=a[i] (当前枚举到第 i 个布丁)。
  • 枚举在r左边的特殊格子位置 l=b[j](b[j]<=a[i]) ,想要覆盖 [l,r] 区间的所有特殊格子,就至少需要另外 rl 个布丁,即 irl+1 ,那么可以得到转移方程1:
  • g[i]=max(g[i],f[i(rl)1]+sum(l,r))
  • 第二种:
  • 设当前转移坐标为 l=a[i] (当前枚举到第 i 个布丁)。
  • 枚举在l右边的特殊格子位置 r=b[j](b[j]<=a[i]) ,想要覆盖 [l,+1r] 区间的所有特殊格子,就至少需要另外 rl 个布丁,即 i+rln ,那么可以得到转移方程2:
  • f[i+rl]=max(f[i+rl],g[i]+sum(l+1,r))
  • 第三种:
  • 显然可以发现转移方程3:
  • f[i]=max(f[i],f[i1]+[sum(a[i],a[i])==1])
  • g[i]=max(g[i],g[i1]+[sum(a[i],a[i])==1])
  • 然后我们发现这两个数组其实可以合并,一起转移,于是就成功缩短了代码。
  • 注意到我们没有考虑一个怪兽不只是占了一格的情况,我们可以在原来转移的基础上引入 L[i],R[i] 表示第i个布丁所属的怪兽的最左边格子编号和最右边格子编号,这样就能解决上述情况。

代码:

#include <bits/stdc++.h>
#define gc getchar()
#define ll long long
#define N 200009
#define M 2009
using namespace std;
int n,m,a[N],b[M],sum[N],dp[N],L[N],R[N];
int read()
{
    int x=1;
    char ch;
    while (ch=gc,ch<'0'||ch>'9') if (ch=='-') x=-1;
    int s=ch-'0';
    while (ch=gc,ch>='0'&&ch<='9') s=s*10+ch-'0';
    return s*x;
}
int main()
{
    n=read(),m=read();
    for (int i=1;i<=n;i++) a[i]=read();
    sort(a+1,a+n+1);
    a[0]=a[1]-1000,a[n+1]=a[n]+1000;
    for (int i=1;i<=n;++i)
        if (a[i]==a[i-1]+1) L[i]=L[i-1];
        else L[i]=i;
    for (int i=n;i;--i)
        if (a[i]==a[i+1]-1) R[i]=R[i+1];
        else R[i]=i;
    sum[0]=0;
    for (int i=1;i<=m;i++) b[i]=read(),sum[b[i]]++;
    sort(b+1,b+m+1);
    for (int i=1;i<=200000;i++) sum[i]+=sum[i-1];
    for (int i=1;i<=n;i++)
    {
        dp[i]=max(dp[i],dp[i-1]);
        int Max;
        if (L[i]==i) Max=dp[i-1]+sum[a[i]]-sum[a[i]-1];
        else Max=-10000000;
        dp[i]=max(dp[i],Max);
        for (int l=1;b[l]<=a[i]&&l<=m;l++)
            if (i-(a[i]-b[l])>=1)
            {
                dp[i]=max(dp[i],dp[L[i-(a[i]-b[l])]-1]+sum[a[i]]-sum[b[l]-1]);
                Max=max(Max,dp[L[i-(a[i]-b[l])]-1]+sum[a[i]]-sum[b[l]-1]);
            }
        for (int r=m;b[r]>=a[i]&&r;r--)
            if (i+b[r]-a[i]<=n)
                dp[R[i+b[r]-a[i]]]=max(dp[R[i+b[r]-a[i]]],Max+sum[b[r]]-sum[a[i]]);
    }
    printf("%d\n",dp[n]);
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值