题目也好,题解也好,都倾向于用小根堆(优先队列)解决问题。复杂度确实是O(nlogn)。通过多次提交发现,题目会卡常,必须用c语言的scanfprintf输入输出,使用c++,即便复杂度更低,仍然超时。
介绍一种O(n)的做法,使用队列。
把桌子分为两类:
A类:坐人数量为0(即有两个空座),
B类:坐人数量1(一个空座)。
根据题意,A类桌子如果有人坐下,那么将会变成B类的桌子。采用贪心的思想,M必须先选B类,仅当B类没有时,才选A类。F则正相反。因此,定义三个队列q0,q1,qb:
q0:存储A类-坐人数量为0
q1:存储B类-坐人数量为1
qb:存储A类转换得到的B类
先扫描一次桌子序列,按编号升序的方式将A、B类,分别存储到q1和q0队列中。
在职员进入餐厅时,先确认使用哪一类桌子。如果选择B类很简单,只要从q0中拿取桌号。注意此时B类桌子坐下一个人后会变成A类桌子,把这张桌子放入队列qb存储(显然这个队列也是升序的)。选择A类时,需从q1和qb中选取编号更小的。
每次选择桌号复杂度均为O(1),因此算法总复杂度为O(n+m)
ps:本题卡常,AC重点并非算法复杂度,而是输入输出方式,c++必须使用scanf的方式。
提交发现,使用queue队列类型仍有未知错误,下面代码使用的是三个静态单链表来模拟队列。
#include <bits/stdc++.h>
using namespace std;
int f0,r0,f1,r1,f3,r3,nex[500005];
void ass(int i)
{
if(i==0)
{
printf("%d\n",f0);
int temp=nex[f0];
if(f3==0)
f3=r3=f0;
else
nex[r3]=f0,r3=f0;
nex[f0]=0;
f0=temp;
}
else if(i==1)
{
if(f3==0||f1&&f1<f3)
{
printf("%d\n",f1);
f1=nex[f1];
}
else if(f1==0||f3&&f3<f1)
{
printf("%d\n",f3);
f3=nex[f3];
}
}
}
int main()
{
int i,j,t,n,m;
char ch;
scanf("%d",&t);
while(t--)
{
scanf("%d",&n);
getchar();
f0=r0=f1=r1=f3=r3=0;/**< 三个单链表模拟队列,指针为空 */
memset(nex,0,sizeof(nex));/**< nex数组实现链式 */
for(i=1; i<=n; i++)
{
ch=getchar();
if(ch=='0')
{
if(f0==0)
f0=r0=i;/**< 空表 */
else
nex[r0]=i,r0=i;/**< 非空表 */
}
else if(ch=='1')
{
if(f1==0)
f1=r1=i;
else
nex[r1]=i,r1=i;
}
}
scanf("%d",&m);
getchar();
for(i=1; i<=m; i++)
{
ch=getchar();
if(ch=='M')
{
if(f3||f1)/**< 两个链有一个不空,就选择B类 */
ass(1);
else
ass(0);/**< 没有B类桌子,选A类 */
}
else
{
if(f0)
ass(0);
else
ass(1);
}
}
}
return 0;
}