最少交换次数
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了最少交换次数相关的知识,希望对你有一定的参考价值。
参考技术A 输入:
第一行包含一个整数T,表示测试用例的数量。接下来会有T个测试用例,对于每个测试用例,包含一个整数N表示数组A[]中元素的个数,接下来一行为数组A[],包含N个用空格分开的整数。
数据规模:(1<=T<=100; 1<=N<=100; 1<=A[i]<=1000)
输出
对于每个测试用例,输出一个整数表示将数组排序所需的最小交换次数。
样例输入输出
解题思路
本题解法如下,首先将数组 A 排序得到 A’ ,遍历数组A中元素,假设当前元素为A[i],
①A[i]在正确的位置(即排序后应在的位置)上,跳过,考虑下一元素。
② 若A[i]不在正确的位置上,假设其排序后的正确位置为j,则将A[i]与A[j]交换。此时i位置上的元素为A[j]的值,记为A[i]’。若A[i]\'是在正确的位置上,则停止,否则,重复②步骤。
example:
遍历数组[1 5 4 3 2],
1在正确的位置上,跳过;
5不在正确的位置上,将其与2交换。2交换到了排序后的正确位置,停止;
4不在正确的位置上,将其与3交换。3交换到了排序后的正确位置,停止;
算法结束,共交换两次。
循环节
解题思路的1小节中,②步骤的循环操作涉及一个理论概念:循环节。
关于排序中最少交换次数的证明(置换环)
关于排序中最少交换次数的证明(置换环)
利用置换环可以进行证明。这里我只做感性的证明。
问题
对于长度为 n n n的数组 a a a, a i ∈ N a_i\\in N ai∈N, a i a_i ai互不相同,每次操作可以选择任意两个位置 i , j i,j i,j交换 a i , a j a_i,a_j ai,aj,要求排序的最少交换次数。
答案
最少交换次数 c n t s w a p = n − c n t c i r c l e cnt_swap=n-cnt_circle cntswap=n−cntcircle
c n t c i r c l e cnt_circle cntcircle 就是数组中置换环的个数。
方法
首先我们对数组进行排序,然后进行映射到 [ 1 , n ] [1,n] [1,n]这个区间。
比如 m ( a j ) = i m(a_j)=i m(aj)=i, 说明 a j a_j aj这个元素最终的位置应该在 i i i。可以理解为 j j j向 i i i连一条有向边。
同时再开一个数组标记位置是否访问过。我们利用 m m m数组暴力找环的个数即可。
时间复杂度: O ( n ) O(n) O(n)
int getSwapCnt(vector<int>&a)
int n = a.size(),cnt=0;
vector<int>b(a);
sort(b.begin(),b.end());
unordered_map<int,int>m;
vector<bool>vis(n);
for(int i=0;i<n;i++) m[b[i]] = i;
for(int i=0;i<n;i++)
if(!vis[i])
int j = i;
while(!vis[j])
vis[j] = true;
j = m[a[j]];
cnt++;
return cnt;
下面这个写法是反向的一个建边,可以允许 a i a_i ai相同。
int a[N],b[N],n;
bool vis[N];
bool cmp(int &x,int &y)
return a[x]<a[y];
int getSwapCnt()
int cnt=0;
for(int i=1;i<=n;i++) b[i] = i;
sort(b+1,b+n+1,cmp);
for(int i=1;i<=n;i++)
if(!vis[i])
int j = i;
while(!vis[j])
vis[j] = true;
j = b[j];
cnt++;
return cnt;
简要证明
显然如果有 n n n个环的话,说明此时已经排序好了。 c n t = n − n = 0 cnt=n-n=0 cnt=n−n=0。
那么我们的目标就是要达到 n n n个环。
每次我们可以选择一个环中的两个结点进行交换,这样环的个数会加1,变成两个。如下图所示:
那么既然每次操作环个数加1,我们已经有 m m m个环了,那么要达到 n n n个环,显然最少次数是 n − m n-m n−m次操作,加 n − m n-m n−m个环。
所以答案就是 n − m n-m n−m。
以上是关于最少交换次数的主要内容,如果未能解决你的问题,请参考以下文章