移动小球
Constraints
Time Limit: 1 secs, Memory Limit: 32 MB
Description
你有一些小球,从左到右依次编号为1,2,3,...,n. 你可以执行两种指令(1或者2)。其中, 1 X Y表示把小球X移动到小球Y的左边, 2 X Y表示把小球X移动到小球Y右边。 指令保证合法,即X不等于Y。 例如,初始状态1,2,3,4,5,6的小球执行1 1 4后,小球1被移动到小球4的左边,即2,3,1,4,5,6。如果再执行2 3 5,结点3将会移到5的右边,即2,1,4,5,3,6。
Input
第一行为一个整数t(0<t<10),表示测试用例个数。每个测试用例的第一行为两个整数n(1<n<=500000)和m(0<m<100000),n表示小球的个数,m为指令条数,以下m行每行为一条指令。
Output
为每个测试用例单独输出一行,从左到右输出最后序列,每个数字后面跟一个空格。
Sample Input
2 6 2 1 1 4 2 3 5 5 1 2 1 5
Sample Output
2 1 4 5 3 6 2 3 4 5 1
-------------------------------------------------------------------------------------------------------------------------------------
本题如果使用移动数组元素的方法,效率低效,而且必然会超时。由于强调的是小球间的相对顺序,而非绝对顺序。可以用left[i]和right[i]分别表示变化为i的小球的左边和右边的小球编号,移动过程可以分成两个步骤:把X移出序列(take);把X重新插入序列。
1 #include <cstdio> 2 3 const int maxn=500000+10; 4 int left[maxn],right[maxn]; 5 void take(int x){//取出x 6 right[left[x]]=right[x]; 7 left[right[x]]=left[x]; 8 } 9 void link(int x,int y){//将x插到y的前面 10 right[x]=y; 11 left[y]=x; 12 } 13 int main(){ 14 int cases,n,m; 15 int type,x,y;//对应移动命令中的三个数 16 scanf("%d",&cases); 17 for(int i=0;i<cases;i++){ 18 scanf("%d%d",&n,&m); 19 for(int j=1;j<=n;j++) //初始化双向链表 20 right[j]=j+1,left[j]=j-1; 21 right[0]=1,left[n+1]=n; 22 23 for(int j=0;j<m;j++){ 24 scanf("%d%d%d",&type,&x,&y); 25 switch(type){ 26 case 1: 27 take(x),link(left[y],x),link(x,y); 28 break; 29 case 2: 30 take(x),link(x,right[y]),link(y,x); 31 break; 32 } 33 } 34 for(int j=0,start=0;j<n;j++){ 35 printf("%d ",right[start]); 36 start=right[start]; 37 } 38 printf("\n"); 39 } 40 return 0; 41 }
思考:一共两种指令,1 X Y将X挪到了Y了左边,2 X Y将X挪到了Y的右边。假设right_Y代表Y右边的球,那么2 X Y是否等价于1 X right_Y?此时如果只实现一个move方法来代替take 和 link方法,并将根据指令移动的那部分代码替换成move方法,此时是否仍然正确?
1 void move(int x,int y){//将x插到y的前面 2 right[left[x]]=right[x]; 3 left[right[x]]=left[x]; 4 right[left[y]]=x; 5 left[x]=left[y]; 6 left[y]=x; 7 right[x]=y; 8 } 9 10 switch(type){ 11 case 1: 12 link(x,y); 13 break; 14 case 2: 15 link(x,right[y]); 16 break; 17 }