[Codeforces436D][DP]Pudding Monsters

33 篇文章 0 订阅

翻译

有一条无限长的数轴
上面放着n个布丁
相邻两个布丁会黏在一起,移动任意一块另外一块也会移动
每次你可以向左或者向右移动一块布丁,这块布丁会一直运动到撞到一块布丁为止
然后他们就黏在一起了
数轴上有m个特殊点
你可以做无数次操作,求最多能覆盖多少个特殊点
n<=100000,m<=2000

题解

容易发现,最后布丁一定会分成一段一段的
其中每一段的左边或者右边一定有一个没有动过的布丁
然后就可以dp辣
设f[i]表示前i个布丁能覆盖的特殊点数量
预处理每个布丁往左第一个与往右第一个的特殊点,不包括与自己重合的
遇到每个布丁,枚举他往左往右覆盖多少个特殊点,容易得到需要多少个布丁来覆盖他们
直接转移即可

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#include<queue>
#include<vector>
#include<ctime>
#include<map>
#define LL long long
#define mp(x,y) make_pair(x,y)
using namespace std;
inline int read()
{
	int f=1,x=0;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
	return x*f;
}
inline void write(int x)
{
	if(x<0)putchar('-'),x=-x;
	if(x>9)write(x/10);
	putchar(x%10+'0');
}
inline void print(int x){write(x);printf(" ");}
int n,m;
int f[100005][2];//在自己位置 不在自己位置 
int a[100005],b[2005];
int pre[100005],nxt[100005];
int fir[100005],ed[100005];
int main()
{
	freopen("a.in","r",stdin);
	freopen("a.out","w",stdout);
	n=read();m=read();
	for(int i=1;i<=n;i++)a[i]=read();
	for(int i=1;i<=m;i++)b[i]=read();
	
	sort(a+1,a+1+n);sort(b+1,b+1+m);fir[1]=1;
	for(int i=2;i<=n;i++)
	{
		if(a[i]!=a[i-1]+1)fir[i]=i;
		else fir[i]=fir[i-1];
	}
	for(int i=n;i>=1;i--)
	{
		if(a[i]!=a[i+1]-1)ed[i]=i;
		else ed[i]=ed[i+1];
	}
	for(int i=1,j=0;i<=n;i++)
	{
		while(b[j+1]<a[i]&&j<m)j++;
		pre[i]=j;
	}
	for(int i=n,j=m+1;i>=1;i--)
	{
		while(b[j-1]>a[i]&&j>1)j--;
		nxt[i]=j;
	}
	for(int i=1;i<=n;i++)
	{
		if(fir[i]==fir[i-1]&&ed[i]==ed[i+1])continue;
		f[i][0]=max(f[i][0],max(f[fir[i]-1][0],f[fir[i]-1][1]));
		for(int j=1;j<=m;j++)
		{
			int pa=pre[i]-j+1;//第j个奇怪格子的编号 
			if(pa<=0)break;
			int pos=a[i]-b[pa];//需要这么多个布丁怪
			if(pos>i)break;
			int gg=fir[i-pos];//凑够这么多个布丁怪的编号
			if(gg<=0)break;
			f[i][0]=max(f[i][0],max(f[gg-1][0],f[gg-1][1])+j); 
		}
		if(b[nxt[i]-1]==a[i])f[i][0]++;
		for(int j=1;j<=m;j++)
		{
			int pa=nxt[i]+j-1;
			if(pa>m)break;
			int pos=b[pa]-a[i];
			if(pos>n-i)break;
			int gg=ed[i+pos];
			if(gg>n)break;
			f[gg][1]=max(f[gg][1],f[i][0]+j);
			if(a[gg]-a[i]+1==j)f[gg][0]=max(f[gg][0],f[i][0]+j);
		}
	}
	printf("%d\n",max(f[n][0],f[n][1]));
	return 0;
}```

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值