poi的好(毒)题。。
题意:
有一个1..n的排列,有两种操作:
(a) 将最后一个数移到最前面
(b) 把第三个数移到最前面
我们将连续进行k次同一个操作称为“一块操作”,表示为ka或kb。
找到一个操作序列使得进行这些操作后,排列变为1,2,3,…,n。
n<=2000
#include<cstring>
#include<cstdlib>
#include<cstdio>
#include<cmath>
#include<iostream>
#define N 2100
using namespace std;
struct node{int o,d;}ans[N*N];
int n,a[N],cnt,las,b[N];
bool ok()
{
for(int i=1;i<=n;i++) if(a[i]!=i) return 0;
return 1;
}
void print()
{
printf("%d\n",cnt);
for(int i=1;i<=cnt;i++)
{
printf("%d",ans[i].d);
if(ans[i].o==1) printf("a ");
else printf("b ");
}
}
void ins(int o,int d)
{
if(o!=ans[cnt].o) {ans[++cnt].o=o;ans[cnt].d=d;}
else
{
ans[cnt].d+=d;ans[cnt].d%=n;
if(ans[cnt].d==0) cnt--;
}
}
bool solve()
{
if(n%2) return 0;
ins(1,n-2);ins(2,2);
for(int i=n;i>4;i-=2) ins(1,n-2),ins(2,2);
if(n-4) ins(1,n-4);
return 1;
}
void make()
{
while(a[1]!=1)
{
ins(1,1);
int t=a[1];a[1]=a[3];a[3]=a[2];a[2]=t;
}
if(a[2]==2) print();
else printf("NIE DA SIE\n");
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
if(ok()) {printf("0\n");return 0;}
if(n==2) {printf("1\n1a\n");return 0;}
if(n==3) {make();return 0;}
for(int k=2;k<=n-2;k++)
{
for(int i=2;i<=n;i++)
if(a[i]==k)
{
ins(1,n-i+1);
int bl=0;
for(int j=i;j<=n;j++) b[++bl]=a[j];
for(int j=1;j<i;j++) b[++bl]=a[j];
for(int j=1;j<=n;j++) a[j]=b[j];
break;
}
int las=0,pre;
for(int i=1;i<=n;i++) if(a[i]==k-1) {las=i;break;}
pre=las;
bool bo=0;
while(las<n)
{
if(las+2<=n) {ins(1,2);ins(2,1);las+=2;}
else {ins(1,1);ins(2,2);las++;bo=1;}
}
int bl=1;
b[1]=k;
for(int i=pre+1;i<=n;i++) b[++bl]=a[i];
for(int i=2;i<=pre;i++) b[++bl]=a[i];
for(int i=1;i<=n;i++) a[i]=b[i];
if(bo) swap(a[2],a[3]);
int oo=1;
}
if(a[2]!=n-1)
{
if(solve()==0) printf("NIE DA SIE\n");
else print();
}
else
{
ins(1,n-3);
print();
}
return 0;
}
题解:
我的第一感觉,b操作的意义是能把某些数留在前三个位置
开始我想把1留在前三个位置,然后把2转到他旁边。再把1,2留在前三个位置,然后把3转到他们旁边。再。。似乎长度大于等于3就留不住了。。
那1..k留在前三个,把k+1转过去不行,就试试把k+1留在前三个,把1..k转过去吧。。
也就是说,核心在于我们已经在某处构造出了1..k,现在想把k+1加入,就用b操作把k+1留在前三个,然后1..k转一圈接上去。这是容易构造的。
注意这种构造方法是要用前三个位置做缓冲的,所以不破坏已经构造出的序列我们只能造出n-2的状态
此时序列要么是
n-2,n-1,n,1,…,n-3(这已经合法了)
要么是
n-2,n,n-1,1,…,n-3
一开始,我觉得此时必定不合法,因为再用b操作会破坏1..n-2的顺序。
于是自信一WA
下载数据后,发现这样一种情况
6 8 7 1 2 3 4 5是有解的!
看6a 2b 6a 2b 6a 2b后会发生什么
5 6 7 8 1 2 3 4
我们观察out的构造方法(捂脸)
把n-1放在第三个位置,然后(n-2)a让n-1去到第一个位置,再用2b把他放到第三个位置。相比操作前,n-1位置不变,而不看第三个位置所有位置右移n-2位,即左移2位。若干次的目的就是通过不断左移两位把原来在第二位的n移到第四位。
显然第二位可以通过若干次左移两位到达第四位的条件是n%2=0,如果这种方法也不行就是NIE DA SIE了。
这就是整个面向数据设计的算法了。
撒花~~