cf1494 C. 1D Sokoban(二分+模拟)

题目链接
在这里插入图片描述
在这里插入图片描述
题意:这是一个一维推箱子的游戏,你站在0这个位置,然后在这个轴上有n个箱子,保证没有箱子在0这个位置,然后有 m 个特殊的位置,你每次推箱子都是推动一个单位,如果你把箱子推向的下一个单位上有箱子,那么那个箱子也会往后挪动一个单位,问:经过你的努力,你最多可以让多少个箱子在特殊位置上。
思路:能推的肯定只有两边的第一个位置,设为位置p,我们每次判断一下把a【p】移到b【i】里那个收益最大就可以了。模拟题嘛,最难的还是代码的实现,注释里都有,最好能自己实现一遍,毕竟一个+1,-1这种边界控制也着实头疼。

#include<bits/stdc++.h>
using namespace std;
const int maxn=2e5+5;
int n,m,T,a[maxn],b[maxn],pre[maxn];
int query1()//向右推 
{
	int res=0,p=0,now;
	for(int i=1;i<=n;++i) if(a[i]>=0){now=p=i;break;}//p是右边第一个箱子位置 
	if(p==0) return 0;
	for(int i=1;i<=m;++i)
	if(b[i]>=a[p]){
		while(now+1<=n&&a[now+1]<=b[i]+now-p+1) ++now;//这里的now-p的意思是对于a【p】这个位置向右推到b【i】的过程中能有now-p块箱子能合并到一起 
		int temp=upper_bound(b+1,b+1+m,b[i]+now-p)-b-i;//再判断这一块合并的箱子能有几个弄到特殊位置 
		res=max(res,temp+pre[n]-pre[now]);//这里别忘了算上后面pre,因为你后面可能本来就有个人箱子本身就在特殊位置了 
	}
	return res;
}
int query2()//向左推,与query1类似 
{
	int res=0,p=0,now;
	for(int i=n;i>=1;--i) if(a[i]<=0){now=p=i;break;}
	if(p==0) return 0;
	for(int i=m;i>=1;--i)
	if(b[i]<=a[p]){
		while(now-1>=1&&a[now-1]>=b[i]-p+now-1) --now;
		int temp=i-(lower_bound(b+1,b+1+m,b[i]-p+now)-b)+1;
		res=max(res,temp+pre[now-1]);
	}
	return res;
}
int main()
{
	scanf("%d",&T);
	while(T--)
	{
		scanf("%d%d",&n,&m);
		for(int i=1;i<=n;++i) scanf("%d",&a[i]);
		for(int i=1;i<=m;++i) scanf("%d",&b[i]);
		for(int i=1;i<=n;++i)//pre数组代码在【1,i】中有多少个箱子本身就在特殊位置了 
		{
			pre[i]=0;
			int pos=lower_bound(b+1,b+1+m,a[i])-b;
			if(pos>=1&&pos<=m&&a[i]==b[pos]) ++pre[i];
			pre[i]+=pre[i-1];
		}
		int ans=query1();
		printf("%d\n",ans+query2());
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值