P6892 [ICPC2014 WF]Baggage 做题记录

有一无穷长的流水线 aa,初始时 i0 和 i>2n,ai=0\forall i\le0\text{ 和 } i>2n ,a_i=0否则,若 ii 为奇数,则 ai=2a_i=2;若 ii 为偶数,则 ai=1a_i=1也就是说,ai=2a_i=2ai=1a_i=1 是交替出现的。

现需要进行若干次 如下 操作,使得 aa 中的所有 非零元素连续 的一段且所有的 11 均在 22 前面

选择两个位置 ppqq,满足 ap0,ap+10a_p\ne0,a_{p+1}\ne0aq=aq+1=0a_q=a_{q+1}=0,将 aqa_q 设为 apa_paq+1a_{q+1} 设为 ap+1a_{p+1},并且将 apa_pap+1a_{p+1} 均设为 00输出时将此操作表示为 p to qppqq 是具体的值)

最小化操作步数,并输出操作序列,出题人将用 Special Judge\text{Special Judge} 来评判您的答案的正确性。

3n1003\le n\le 100

容易证明操作次数下界是 nn,因为设相邻两个类型相同的包裹有 tt 组,初始 t=0t=0,最终要令 t=2n2t=2n-2。每次操作取出包裹时 tt 不会增加,而放回则最多增加 22,并且第一次操作只能令 tt 增加 11,所以操作次数下界为 1+2n32=n1+\lceil \frac{2n-3}{2}\rceil=n

手模一下 3n73\le n\le 7 的情况:

OOOOOOBABABA
OOOOABBOOABA
OOOOABBBAAOO
OOAAABBBOOOO
OOOOOOOOBABABABA
OOOOOOABBABABOOA
OOOOOOABBAOOBBAA
OOOOOOAOOABBBBOO
OOOOOOAAAABBBBOO
OOOOOOOOOOBABABABABA
OOOOOOOOABBABABABOOA
OOOOOOOOABBAOOBABBAA
OOOOOOOOABBAABBOOBAA
OOOOOOOOAAAAABBBBBOO
OOOOOOOOOOOOBABABABABABA
OOOOOOOOOOABBABABABABOOA
OOOOOOOOOOABBABABAOOBBAA
OOOOOOOOOOABBOOABAABBBAA
OOOOOOOOOOABBAAABOOBBBAA
OOOOOOOOOOAOOAAABBBBBBAA
OOOOOOOOOOAAAAAABBBBBBOO
OOOOOOOOOOOOOOBABABABABABABA
OOOOOOOOOOOOABBABABABOOABABA
OOOOOOOOOOOOABBABAOOBBAABABA
OOOOOOOOOOOOABBABAABBBAABOOA
OOOOOOOOOOOOABBAOOABBBAABBAA
OOOOOOOOOOOOABBAAAABBBOOBBAA
OOOOOOOOOOOOAOOAAAABBBBBBBAA
OOOOOOOOOOOOAAAAAAABBBBBBBOO

不难发现,n>3n>3 时,第一步总是 2n-2 to -1,并且最后包裹总是停留在 [1,2n2][-1,2n-2]。那么考虑构造 n8n\ge 8 的操作序列:

OOO...OOOBA[BABABA...BABA]BABA
OOO...OABBA[BABABA...BABA]BOOA
OOO...OABBA[OOBABA...BABA]BBAA

此时若我们递归构造,将中括号内变成 AAA...AB...BBBOO,那么可以这样构造接下来的操作序列:

OOO...OABBA[AAA...AB...BBBOO]BBAA
OOO...OAOOA[AAA...AB...BBBBB]BBAA
OOO...OAAAA[AAA...AB...BBBBB]BBOO

不难发现,这样在递归下去之前用了两次操作,递归完之后又用了两次操作,递归到到子问题 nn 减少了 44,所以总共恰好使用了 nn 次操作。

边界条件显然是 3n73\le n\le 7,因为此时递归到到子问题 nn 就会 3\le 3,不满足最后包裹停留在 [1,2n2][-1,2n-2]

代码如下:

#include <iostream>
#include <cstdio>

using namespace std;

inline void mov(int x,int y)
{
	printf("%d to %d\n",x,y);
}

void inline run3(int l)
{
	mov(l+1,l-2);
	mov(l+4,l+1);
	mov(l+2,l-4);
}

void inline run4(int l)
{
	mov(l+5,l-2);
	mov(l+2,l+5);
	mov(l-1,l+2);
	mov(l+6,l-1);
}

void inline run5(int l)
{
	mov(l+7,l-2);
	mov(l+2,l+7);
	mov(l+5,l+2);
	mov(l-1,l+5);
	mov(l+8,l-1);
}

void inline run6(int l)
{
	mov(l+9,l-2);
	mov(l+6,l+9);
	mov(l+1,l+6);
	mov(l+5,l+1);
	mov(l-1,l+5);
	mov(l+10,l-1);
}

void inline run7(int l)
{
	mov(l+7,l-2);
	mov(l+4,l+7);
	mov(l+11,l+4);
	mov(l+2,l+11);
	mov(l+8,l+2);
	mov(l-1,l+8);
	mov(l+12,l-1);
}

inline void slove(int l,int r)
{
	if((r-l+1)/2==3)
	{
		run3(l);
		return;
	}
	if((r-l+1)/2==4)
	{
		run4(l);
		return;
	}
	if((r-l+1)/2==5)
	{
		run5(l);
		return;
	}
	if((r-l+1)/2==6)
	{
		run6(l);
		return;
	}
	if((r-l+1)/2==7)
	{
		run7(l);
		return;
	}
	mov(r-2,l-2);
	mov(l+2,r-2);
	slove(l+4,r-4);
	mov(l-1,r-5);
	mov(r-1,l-1);
}

int main()
{
	int n;
	scanf("%d",&n);
	slove(1,n*2);
	return 0;
}