- 一张无限长的格子纸条,其中(1sim 2n)位奇数位填着"B",偶数位填着"A",剩余位没有字母。
- 一次移动操作可以将连续两个有字母的格子中的字母剪切到连续两个没有字母的格子中。
- 要求构造一组移动步数最少方案,使得所有"A","B"相连且所有"A"在所有"B"前面。
- (3le nle100)
构造题
首先,样例给了我们很明显的提示:最少的移动步数就是(n)。
所以说,关键在于如何构造。
我们发现一开始肯定可以进行若干次操作,每次先将最右边的一个"AB"移到左边,再将最左边的一个"BA"移到右边。
这样不断搞下去最终中间会出现一段无法如此操作的部分。
然后又发现需要根据(n)模(4)的余数分成(4)类情况讨论,其中(n\%4=0/1)的情况样例中有了,而且本身也非常简单。
而(n\%4=2/3)的情况关键在于手玩出(n=6)和(n=7)的情况。
注意,(n\%4=3)的时候并不是在(n=3)的情况基础上推得的(当然由于可能存在(n=3),这一部分的手玩也是不可避免的),而是由(n=7)推得的!所以不要像我那样试着用(n=3)来推(n=7)直接懵了(1)个小时。。。
处理完中间部分后,不同情况的操作又变得统一了起来,每次先将左边最靠右的一个"BB"移到右边,再将右边最靠左的一个"AA"移到左边。
具体实现详见代码吧,因为具体的构造过程也很难讲清楚。
代码:(O(n))
#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define Move(x) (printf("%d to %d
",x,z),z=x)//将x移到空位上
using namespace std;
int n;
int main()
{
RI i,x,y,z=-1;if(scanf("%d",&n),n==3) return Move(2),Move(5),puts("3 to -3"),0;//特判n=3,由于要用到-3位置,所以无法推广
for(i=1,x=2*n-2,y=3;i<=n/4-(n%4>=2);++i) Move(x),x-=4,Move(y),y+=4;//通用的开始
switch(n%4)//按余数分类讨论
{
case 0:y=x-2,x+=5;break;case 1:Move(x+2),y=x-4,x+=5;break;
case 2:Move(x),Move(x-3),Move(x-8),Move(x-4),y=x-10,++x;break;
case 3:Move(x-4),Move(x-7),Move(x),Move(x-9),Move(x-3),y=x-12,++x;break;
}
for(i=1;i<=n/4;++i) Move(y),y-=4,Move(x),x+=4;return 0;//通用的结束
}