PAT1067. Sort with Swap(0, *) (25) 并查集
题目大意
给定一个序列, 只能进行一种操作: 任一元素与 0 交换位置, 问最少需要多少次交换.
思路
最优解就是每次 0 都和所在位置本应在的元素交换位置, 共 n - 1 次, 但是在交换中 0 可能会被交换到 0 号位置.
仔细思考一下, 其实每次换到 0 就是一个图的连通分量, 按照输入的次序和输入的值, 可以求出图共有多少个联通分量.
每个联通分量若要换回去, 需要 n - 1 次交换, 但是只能用 0 交换, 所以不含 0 且 结点个数不为 1 的连通分量需要进行 加 2 操作 (把 0 换进来, 排序, 再把 0 换出去).
代码
#include <cstdio>
#include <cstring>
#define MAXN 100100
using namespace std;
int arr[MAXN];
int cnt[MAXN];
int getFather(int a){
if(a == arr[a])
return a;
return arr[a] = getFather(arr[a]);
}
void merge(int a, int b){
a = getFather(a);
b = getFather(b);
if(a < b)
arr[b] = a;
else
arr[a] = b;
}
int main(){
int nNum;
scanf("%d", &nNum);
for(int i = 0; i < nNum; arr[i] = i++);
for(int i = 0; i < nNum; i++)
arr[i] = i;
for(int i = 0; i < nNum; i++){
int val;
scanf("%d", &val);
merge(i, val);
}
for(int i = 0; i < nNum; i++){
cnt[arr[i]]++;
}
int sum = 0;
for(int i = 0; i < nNum; i++){
if(arr[i] != i)
continue;
sum += cnt[i] - 1;
if(i != 0 && cnt[i] != 1)
sum += 2;
}
printf("%d", sum);
return 0;
}