转载自http://blog.sina.com.cn/s/blog_ce1b01420102visv.html
Given any permutation of the numbers {0, 1, 2,..., N-1}, 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 (<=105) followed by a permutation sequence of {0, 1, ..., N-1}. 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
-----------------------------------------------------------------------------------------------------------------------------------------------
本题比较水,几行代码就解决了,但需要用到表排序的一些知识。
预备知识:N个数字的排列有若干个独立的环组成。其中N个数字表示1~N或者0~N-1等类似递增的数列。
根据题意,我选取了3个数字(0,1,2)的排列进行分析
假定数组内存放的是下个数组的下标,已021为例,A[0]=0,刚好指向自己,所以0是只含1个元素的环,A[1]=2 指向下标为2的元素,而A[2]=1,指向下标为1的元素,所以,A[1]和A[2]组成了一个环 。
分析:
第一种情况:所有的单个数字都是独立的环,例如012三个数字已经排好了位置,也就是说如果独立环就1个元素就不需要交换位置。
第二种情况:N个数字组成了1个环,例如201和120。发现都需要2次交换排成012,猜想:交换次数 = 环的元素个数 - 1。
第三种情况: 有2个环,分别是021、102、210。每种情况都是有1个含有1个元素的环和含有2个元素的环组成的,按第1和第2种情况猜测,1个元素的环不需要交换,含有2个环的需要交换一次,所以结论应该都是1次交换,而实际情况102和210确实是1次,021却是3次。
为什么021会不同?那是因为Swap(0,*)是跟0相关的操作,而0一开始就站在了应该待的位置,如果0不动,后面2个元素是没法移动的。为了能使这种情况也变成一般性,我们需要先把0和后面任意元素交换位置,即把0加入到其他环中,对一个含有N个元素的环来说,随着0的加入,就变成了N+1个元素的环,然后就可以按普通情况来处理了,对N+1个元素的环,总共需要移动N次,加上第一步0和环中任意元素交换的那一次,总共需要N + 1次。
按上面粗略的分析,这道题就可以看成判断独立数环的问题。如果环中只含有1个元素,则不需要交换;如果有N个元素的环且包含0,则需要移动N - 1次;
如果N个元素且不包含0,需要移动N + 1次。题目要求出最少次数,所以并不需要模拟移动,只需判断环有几个元素组成,是否包含0即可。
==================================================================
#include < cstdio >
int *A;
int Cal(int A[], int i, int N); //计算需要交换几次
int main()
{
int N;
int Total = 0;
scanf("%d", &N);
A = new int[N];
for(int i = 0; i < N; i++)
scanf("%d", &A[i]);
for(int i = 0; i < N; i++){
if (A[i] == i) continue; //数环只含1个元素
int S = Cal(A, i, N); //以i为环的起点进行扫描
Total += S; //更新交换的次数
}
printf("%d\n", Total);
return 0;
}
int Cal(int A[], int i, int N)
{
int S = 0;
bool Flag = false; //用来判断是否含0
while(A[i] != i){ //被访问过的元素下标=元素值
if (i == 0) Flag = true;
int t = i; //保存原始下标
i = A[i]; //让i指向下一个元素的下标
A[t] = t; //访问过的元素做记号
S++;
}
if (Flag) return S - 1; //含有0
else return S + 1; //不含0
}