10-排序6 Sort with Swap(0, i) (25 分)

Given any permutation of the numbers {0, 1, 2,..., N1}, it is easy to sort them in increasing order. But what if Swap(0, *) is the ONLY operation that is allowed to use? For example, to sort {4, 0, 2, 1, 3} we may apply the swap operations in the following way:

Swap(0, 1) => {4, 1, 2, 0, 3}
Swap(0, 3) => {4, 1, 2, 3, 0}
Swap(0, 4) => {0, 1, 2, 3, 4}

Now you are asked to find the minimum number of swaps need to sort the given permutation of the first N nonnegative integers.

Input Specification:

Each input file contains one test case, which gives a positive N (≤) followed by a permutation sequence of {0, 1, ..., N1}. All the numbers in a line are separated by a space.

Output Specification:

For each case, simply print in a line the minimum number of swaps need to sort the given permutation.

Sample Input:

10
3 5 7 2 6 4 9 0 8 1

Sample Output:

9

一、思路

(一)规律:

  1.N个数字排列整齐的条件是Position[X] == X

  2.N个数字的全排列(permutation)必定构成若干个闭环。

  3.Swap(0, X)就相当于Swap(Position[0], Position[X])

 

 

(二)说明

0.规律2

如样例:

 X     3 5 7 2 6 4 9 0 8 1

   Position 0 1 2 3 4 5 6 7 8 9    

   Position[7] = 0; Position[0] = 3; Position[3] = 2; Position[2] = 7; Position[7] = 0 …………

 

1.由规律2 && 规律3容易得到,针对0的元素交换只能在包含0的闭环中进行。

如: 对于7-0-3-2-7闭环中,无论怎么交换,最后情况

Position[7] = 7;

Position[0] = 0;

Position[3] = 3;

Position[2] = 2

 

2.所以如果排序序列中有多个环,就要让0进入不包含0的环,进入的方法也很简单:

Swap(Position[0], Postion[X]) where X is any member of any anther loop.

 

3.按照题意(4,0,2,1,3)交换顺序的提示,本题应该是贪心(greedy)算法的意思。

即每次Swap,把Position[0]对应的元素拉回原位。

如:

1.Position[0] = 3 

2.Position[3] = 2;

3.Swap( Position[0], Position[3] )

Outcome:

Position[0] = 2;

Position[3] = 3; /*correct position*/

 

4. 按照贪心算法,可以简化步骤2,因为每次包含0的环在进行若干次Swap后所有元素均有序,

所以剩下的不包含0的环的元素有一个共同特点——即Position[X] !=X

 

 

(三)步骤

步骤一、

根据说明3复位含0的闭环中的所有元素

步骤二、

查找仍在错误位置的X,交换(0,X);若找不到则退出循环

步骤三、

Swap(0,X),回步骤一

 

 

 

二、具体实现

(一)数据结构的选择

1.数组

 1 #include <cstdio>
 2 #include <algorithm>
 3 
 4 using namespace std;
 5 const int MAXN = 1e5 + 1;
 6 
 7 int Position[MAXN];
 8 void ZeroSort(int N);
 9 
10 int main()
11 {
12     int N, tmp;
13     scanf("%d", &N);
14     
15     for (int i=0; i<N; i++) {
16         scanf("%d", &tmp);
17         Position[tmp] = i;
18     }
19     
20     
21     
22     ZeroSort( N );
23     
24     
25     return 0;
26     
27 }
28 
29 void ZeroSort(int N)
30 {
31     int P, PreZeroPos, cnt;
32     cnt = 0;
33     
34     while (1) {
35         PreZeroPos = Position[0];
36         
37         if (Position[0] != 0) {
38             swap( Position[0], Position[Position[0] ] );
39         }
40         else {
41             for (P=0; P<N; P++) if(Position[P] != P) break;
42             swap( Position[0], Position[P] );
43         }
44         if (PreZeroPos == Position[0]) {
45             break;
46         }
47         cnt++;
48         }
49     
50     printf("%d\n", cnt);
51         
52 }
53    
V1

2.数组+std::set(内部实现是AVL树)

 1 #include <cstdio>
 2 #include <algorithm>
 3 #include <set>
 4 using namespace std;
 5 const int MAXN = 1e5 + 1;
 6 
 7 int Position[MAXN];
 8 
 9 
10 
11 void ZeroSort(int N, set<int> St);
12 
13 
14 int main()
15 {
16     int N, tmp;
17     set<int> St;
18     
19     scanf("%d", &N);
20     
21     for (int i=0; i<N; i++) {
22         scanf("%d", &tmp);
23         Position[tmp] = i;
24         if (Position[i] != i and i!=0) {
25             St.insert(i);
26         }
27     }
28     
29     
30     
31     ZeroSort( N, St );
32     
33     return 0;
34     
35 }
36 
37 
38 
39 void ZeroSort(int N, set<int> St)
40 {
41     int PreZeroPos, cnt, X;
42     cnt = 0;
43     
44     //O(N)
45     while (St.size() != 0) {
46         PreZeroPos = Position[0];
47         
48         if (PreZeroPos != 0) {
49             swap( Position[0], Position[ PreZeroPos ] );
50             St.erase( St.find(PreZeroPos) );
51         }
52         
53         else {
54             X = *(St.begin());
55             swap( Position[0], Position[X] );
56         }
57         
58         cnt++;
59         
60         }
61     
62     printf("%d\n", cnt);
63         
64 }
65    
V2

3.双链表实现

 1 #include <cstdio>
 2 #include <algorithm>
 3 using namespace std;
 4 
 5 const int MAXN = 1e5 + 1;
 6 int Position[MAXN];
 7 
 8 struct LNode {
 9     int Data;
10     LNode * Next;
11     LNode * Before;
12 } WrongNumList[MAXN];
13 
14 void initWroNumList(int N);
15 void ZeroSort(int N);
16 void DeleteRightNum(int Dig);
17 
18 int main()
19 {
20     int N, tmp;
21     
22     scanf("%d", &N);
23     initWroNumList(N);
24     
25     for (int i=0; i<N; i++) {
26         scanf("%d", &tmp);
27         Position[tmp] = i;
28         if (Position[i] == i and i!=0)
29         {
30             DeleteRightNum(i);
31         }
32         
33     }
34     
35     ZeroSort( N );
36     
37     
38     return 0;
39     
40 }
41 
42 void initWroNumList(int N)
43 {
44     WrongNumList[0].Data = 0;
45     for (int i = 1; i <= N; i++) //N为哨兵
46     {
47         WrongNumList[i].Data = i;
48         WrongNumList[i-1].Next = &( WrongNumList[i] ) ;
49         WrongNumList[i].Before = & ( WrongNumList[i-1] );
50     }
51     
52     
53     
54     
55     
56 }
57 
58 
59 void DeleteRightNum(int Dig)
60 {
61     int PreNum = WrongNumList[Dig].Before->Data;
62     int NextNum = WrongNumList[Dig].Next->Data;
63     
64     WrongNumList[PreNum].Next = WrongNumList[Dig].Next;
65     WrongNumList[NextNum].Before = WrongNumList[Dig].Before;
66     
67     WrongNumList[Dig].Next = NULL;
68     WrongNumList[Dig].Before = NULL;
69     
70 }
71 
72 
73 void ZeroSort(int N)
74 {
75     int PreZeroPos, cnt, X;
76     cnt = 0;
77     
78     //O(N)
79     while ( WrongNumList[0].Next->Data != N) {
80         PreZeroPos = Position[0];
81         if (PreZeroPos != 0) {
82             swap( Position[0], Position[ PreZeroPos ] );
83             DeleteRightNum(PreZeroPos);
84         }
85         else {
86             X = WrongNumList[0].Next->Data ;
87             swap( Position[0], Position[X] );
88         }
89         
90         cnt++;
91         
92     }
93     
94     printf("%d\n", cnt);
95     
96 }
V3

 

(二)分析

Version1 超时

分析:

如何识别Position[X] != X,Version 1中扫描了整个数组,O(N)

外面的大循环显然也是O(N)的,因为最坏情况下S要Swap N-1 次;

总的复杂度O(N^2),N=1e5不可接受。

 

Version2 AC

分析:

1.步骤一、

复位X后删除MalPosSt中的元素,表示该元素已经复位。O(logN)

2.步骤二、

任选MalPosSt中元素,此处我选了*( MalPosSt.begin() )。 O(1)

3.步骤三、

O(1)

外面大循环O(N),总的复杂度为O(NlogN

 

Version3 AC

O(N)

 

 

posted @ 2019-06-02 09:45  Acoccus  阅读(169)  评论(0编辑  收藏  举报