2 solutions

  • -4
    @ 2024-2-6 19:32:51
    #include<iostream>
    #include<cstdio>
    using namespace std;
    const int N = 1010, M = 2e6 + 10;
    int n, A[N];
    int T, B[M];
    int fst, lst, pre[N], nex[N], r;
    int getlst(){
    	if(r == 0) return lst;
    	return fst;
    }
    int getfst(){
    	if(r == 0) return fst;
    	return lst;
    }
    int getsec(){
    	if(r == 0) return nex[fst];
    	return pre[lst];
    }
    int getnex(int x){
    	if(r == 0) return nex[x];
    	return pre[x];
    }
    void opt1(){
    	B[++T] = 1;
    	r ^= 1;
    }
    void opt2(){
    	B[++T] = 2;
    	int x = getsec(), l = getlst(), f = getfst(), y = getnex(x);
    	if(r == 0){
    		pre[x] = l;
    		nex[l] = x;
    		pre[y] = f;
    		nex[f] = y;
    		lst = x;
    	}else{
    		nex[x] = l;
    		pre[l] = x;
    		nex[y] = f;
    		pre[f] = y;
    		fst = x;
    	}
    }
    void opt3(){
    	B[++T] = 3;
    	int x = getsec(), l = getlst(), f = getfst();
    	if(r == 0){
    		pre[f] = l;
    		nex[l] = f;
    		pre[x] = 0;
    		nex[f] = 0;
    		lst = f;
    		fst = x;
    	}else{
    		nex[f] = l;
    		pre[l] = f;
    		nex[x] = 0;
    		pre[f] = 0;
    		lst = x;
    		fst = f;
    	}
    }
    void print(){
    	printf("\n");
    	int u = getfst();
    	while(u != getlst()){
    		printf("%d ",u);
    		u = getnex(u);
    	}
    	printf("%d\n",getlst());
    }
    void init(){
    	for(int i = 1; i <= n; i += 1){
    		pre[i] = i - 1;
    		if(i != n) nex[i] = i + 1;
    	}
    	fst = 1, lst = n;
    }
    int main(){
    	scanf("%d",&n);
    	for(int i = 1; i <= n; i += 1)
    		scanf("%d",&A[i]);
    	init();
    	if(A[1] == 1) opt1();
    	else while(getlst() != A[1]) opt2();
    	for(int i = 2; i < n - 1; i += 1){
    		if(getfst() == A[i]){
    			opt2();
    			opt1();
    			while(getlst() != A[1]) opt2();
    			if(i == n - 2){
    				opt1();
    				break;
    			}else{
    				opt2();
    				opt1();
    				while(getlst() != A[i]) opt2();
    			}
    		}else{
    			while(getlst() != A[i]) opt2();
    			opt1();
    			while(getsec() != A[i - 1]) opt2();
    			opt1();
    		}
    		//print();
    	}
    	if(getfst() == A[n]){
    		opt2();
    		opt3();
    	}else if(getfst() != A[1]){
    		opt2();
    		opt1();
    		while(getlst() != A[1]) opt2();
    		opt1();
    	}
    	printf("%d\n",T);
    	for(int i = 1; i <= T; i += 1)
    		printf("%d ",B[i]);
    	//print();
    	return 0;
    }
    
    • -5
      @ 2024-2-6 19:26:24

      Solution

      Permutation

      题意简述:使用三种特定的操作完成对指定序列的排序操作

      我们的答题思路是,采用类似于数学归纳的方法,先把前 kk 个数字排好序,然后把第 k+1k + 1 个数字排到最后面。我们要求每一次大的操作结束后,前 kk 个排好序的数字恰好位于后 kk 位。

      下面来分情况讨论一下:

      第一个数字的情形:

      第一个数字恰好在当前序列的首位

      直接翻转整个序列即可。

      第一个数字不在当前序列的首位

      直接一直做操作二直到第一个数字位于最后

      第 2 至第 n - 2 个数字的情形:

      假设已经排好了前 kk 个,现在要排第 k+1k + 1 个。

      第 k + 1 个数字恰好位于当前序列的首位

      这样的话我们的操作二没办法影响到第 k+1k + 1 个数字,所以要翻转序列。那么先做一次操作二,这样就能保证翻转以后,首位不会是前 kk 个数的其中一个,从而他们的相对顺序不会改变。然后做操作一,翻转序列。这时,容易发现,其实第 k+1k + 1 个数已经接在了前 kk 个数的后面,所以接下来只需要一直做操作二直到把第一个数丢到了结尾即可。然后再做一次操作二,再翻转回来就OK。

      (这一段比较绕,读者可以自行在草稿纸上写写画画帮助自己理解)

      k+1k + 1 个数字不在当前序列的首位

      首先,把第 k+1k + 1 个数字丢到最后一位,然后翻转整个序列。这样的话,操作二就不会影响到第 k+1k + 1 个数字。然后,一直做操作二,直到第 kk 个数字被丢到了第二个位置,最后翻转整个序列即可。

      最后两个数字的情形:

      nn 个数字位于首位

      直接做一次操作二,然后做一次操作三即可。

      n1n - 1 个数字位于首位

      类似于上述第 k+1k + 1 个数字位于首位的情形,只是最后少做一次操作二。

      复杂度分析の回合

      容易发现,每一次把新的数接到原先排好序的序列的最后的过程,实际上最多做 2n2n 次操作(事实上仔细分析应该是 n+n+ 常数级别,这里放的宽松一点显得我还像个人),而且操作三实际上最多用到一次(放到 1010 次是因为这样的话你的第一档部分分就可以用一些其他的方法水过去,具体怎么水过去留作读者练习)。

      然而单次操作直接搞是 O(N)O(N) 的,总复杂度可能达到 O(N3)O(N^3)。然而可以使用带翻转标记的链表维护这个东东,可以实现 O(1)O(1) 的单词操作,总复杂度降至 O(N2)O(N^2)。具体实现留作读者练习。

      • 1

      Information

      ID
      310
      Time
      1000ms
      Memory
      256MiB
      Difficulty
      10
      Tags
      (None)
      # Submissions
      25
      Accepted
      0
      Uploaded By