输入样例1:
5
3 1 2 5 4
输出样例1:
3
输入样例2:
5
5 4 3 2 1
输出样例2:
2
这道题目的思路比较难想,他是与图论相结合的一道题,我们把这n个位置看成n个点,对于第i个点的值a[i],让第i个点向第a[i]个点连一个有向边代表第i个点上的值最终位置是第a[i]个点,因为每个点只会有一个最终位置且所在位置只会是一个点的最终位置,所以按照上述方法连完边后对于每个点只会有一条出边以及一条入边,也就是会形成若干个环,先按照样例1进行画边并进行分析:
这就是按照上面的方法所建的图,下面我们来看一下交换两个点之后再建图会发生什么样的变化
这个时候应该分两种情况来进行讨论,一种是交换一个环内的两个点,另一种就是所交换的两个点位于两个环内
第一种情况:(交换的两个点位于一个环内,比如1,2位置上的点)
则1位置上的点会指向2位置点所指向的点,也就是1,而2位置会指向1位置所指向的点,也就是3,这样我们会发现1形成了自环,所以环的数量减少1
第二种情况:(交换的两个点位于不同的两个环上,比如1,5位置上的点)
则1位置上的点会指向5位置点所指向的点,也就是4,而5位置会指向1位置所指向的点,也就是3,这样我们会发现这两个点所在的环形成了一个大环,也就是环的数量减少了1
通过这两种情况的分析,我们会发现若交换的两个点位于一个环内,则环的数量减少1,若交换的两个点位于不同的两个环上,则环的数量减少1,而我们最终的情况是所有点形成了自环,也就是环的数量为n,所以我们应该每次交换一个环内的两个点使得环的数量加1.所以答案就是n-一开始环的数量
如何计算环的数量,这里用的是一个比较巧妙的方法,详情参考代码:
#include<cstdio>
#include<iostream>
#include<cstring>
#include<vector>
#include<algorithm>
#include<map>
#include<cmath>
#include<queue>
using namespace std;
const int N=10003;
int a[N];
bool vis[N];
int main()
{
int n;
cin>>n;
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
int cnt=0;
for(int i=1;i<=n;i++)
{
if(!vis[i])
{
cnt++;
int t=i;
while(!vis[t])
{
vis[t]=true;
t=a[t];
}
}
}
printf("%d",n-cnt);
return 0;
}