将 { 0, 1, 2, ..., N-1 } 的任意一个排列进行排序并不困难,这里加一点难度,要求你只能通过一系列的 Swap(0, *) —— 即将一个数字与 0 交换 —— 的操作,将初始序列增序排列。例如对于初始序列 { 4, 0, 2, 1, 3 },我们可以通过下列操作完成排序:
- Swap(0, 1) ⟹ { 4, 1, 2, 0, 3 }
- Swap(0, 3) ⟹ { 4, 1, 2, 3, 0 }
- Swap(0, 4) ⟹ { 0, 1, 2, 3, 4 }
本题要求你找出将前 N 个非负整数的给定排列进行增序排序所需要的最少的与 0 交换的次数。
输入格式:
输入在第一行给出正整数 N (≤);随后一行给出{ 0, 1, 2, ..., N-1 } 的一个排列。数字间以空格分隔。
输出格式:
在一行中输出将给定序列进行增序排序所需要的最少的与 0 交换的次数。
输入样例:
10
3 5 7 2 6 4 9 0 8 1
输出样例:
9
3 5 7 2 6 4 9 0 8 1
0 1 2 3 4 5 6 7 8 9
由于给出的是全排序,那么就可以看成把a[i]移到以a[i]下标的位置
可以找循环节即有(3,0,7,2),(5,1,9,6,4),(8)三个
第一个节(包含0)需要循环节长度-1次操作,即可把其它元素移到对应的位置
第二个节(不包含0)需要循环节长度+1次操作,可以看成向循环节中加入0(操作次数加1),
那么循环节长度加1(交换时次数要加1),总共比包含0的情况多操作两次
第三个节是一个元素,那么不需要任何操作。
#include <bits/stdc++.h> using namespace std; const int N=100005; int vis[N],a[N],b[N]; void dfs(int u,int f) { if(vis[u]) return; vis[u]=f; dfs(b[u],f); } int main() { int n,cnt=1; scanf("%d",&n); for(int i=0;i<n;i++) scanf("%d",&a[i]),b[a[i]]=i; for(int i=0;i<n;i++) if(!vis[i]) dfs(a[i],cnt),cnt++; map<int,int> ma; //for(int i=0;i<n;i++) printf("%d%c",vis[i],i==n-1?' ':' '); for(int i=0;i<n;i++) ma[vis[i]]++; int ans=0; for(int i=1;i<cnt;i++) { if(ma[i]==1) continue;//循环节里面只有一个元素 if(i==vis[0]) ans+=ma[i]-1;//循环节里面有0元素 else ans+=ma[i]+1;//循环节里面没有0元素 } printf("%d ",ans); return 0; }