1D Sokoban
\(1D Sokoban\)
在数轴上有n个箱子,有n个关键点,给出箱子和关键点的坐标,你现在站在0的位置,可以任意移动推箱子,求解最后能够使得多少箱子在关键点上,注意人不能越过箱子。
那么我们发现你最后推到的位置一定是一个关键点,然后箱子会覆盖一个区间的关键点,那么我们可以利用队列维护关键点集合,然后统计区间长度即可。
#include<bits/stdc++.h>
#define LL long long
#define V inline void
#define I inline int
#define FOR(i,a,b) for(register int i=a,end##i=b;i<=end##i;++i)
#define REP(i,a,b) for(register int i=a,end##i=b;i>=end##i;--i)
#define go(i,x) for(int i=hed[x];i;i=e[i].pre)
using namespace std;
inline int read()
{
char x='\0';
int fh=1,sum=0;
for(x=getchar();x<'0'||x>'9';x=getchar())if(x=='-')fh=-1;
for(;x>='0'&&x<='9';x=getchar())sum=sum*10+x-'0';
return fh*sum;
}
const int N=4e5+9;
int T,n,m;
struct node{
int pos,tp,val;
}a[N],b[N];
int acnt,bcnt;
inline bool cmp1(node x,node y)
{
if(x.pos!=y.pos)return x.pos>y.pos;
return x.tp<y.tp;
}
inline bool cmp2(node x,node y)
{
if(x.pos!=y.pos)return x.pos<y.pos;
return x.tp<y.tp;
}
map<int,int>mp;
int main()
{
T=read();
while(T--)
{
mp.clear();
acnt=0,bcnt=0;
n=read(),m=read();
FOR(i,1,n)
{
int x=read();
if(x<0)a[++acnt]={x,0},mp[x]=acnt;
else b[++bcnt]={x,0},mp[x]=bcnt;
}
int sva=0,svb=0;
FOR(i,1,m)
{
int x=read();
if(x<0)
{
a[++acnt]={x,1};
if(mp.find(x)!=mp.end())
a[mp[x]].val=1,sva++;
}
else
{
b[++bcnt]={x,1};
if(mp.find(x)!=mp.end())
b[mp[x]].val=1,svb++;
}
}
sort(a+1,a+acnt+1,cmp1);
sort(b+1,b+bcnt+1,cmp2);
int ansa=sva,ansb=svb;
int len=0;
queue<int>q;
FOR(i,1,acnt)
{
if(a[i].tp==0)len++,sva-=a[i].val;
else
{
q.push(a[i].pos);
while(q.size()&&q.front()>=a[i].pos+len)q.pop();
ansa=max(ansa,sva+(int)q.size());
}
}
len=0;
while(!q.empty())q.pop();
FOR(i,1,bcnt)
{
if(b[i].tp==0)len++,svb-=b[i].val;
else
{
q.push(b[i].pos);
while(q.size()&&q.front()<=b[i].pos-len)q.pop();
ansb=max(ansb,svb+(int)q.size());
}
}
printf("%d\n",ansa+ansb);
}
return 0;
}