HF市地下有一份宝藏,数不清的黄金、钻石……。令人遗憾的是,千年来未曾有人得到过这份宝藏。其中一个主要原因就是,要得到这份宝藏之前,首先需要通过一个迷宫。
这个迷宫的结构非常复杂,一共有N个秘密房间。广为流传的一种说法是,必须在1天之内将这N个房间的门全部打开,才能找到迷宫的出口,否则就会葬身于其中。传说中同时
指出,这些房间之间有着错综复杂的联系。对于每个房间而言,仅有一把能够打开这个房间的钥匙,这把钥匙可能位于这N个房间中的任意一个。每当一个房间被打开后,就可
以得到房间里面的钥匙,并以打开其他的房间。被世人称为有史以来最天才的探险家小雪准备于近日进入迷宫探险。为了确保万无一失,他携带了足量的新型炸弹,每枚炸弹可
以将一个房间的门直接炸掉。由于炸弹的威力太大,可能破坏整个迷宫的结果,小雪准备用最少的炸弹打开所有的房间,你能够帮助小雪计算一下吗?
Input
第一行包含一个整数N(N<=1000000),表示迷宫中房间的数目。接下来的N行,每行包含一个整数,表示能够打开第i个房间的钥匙的位置。
Output
仅包含一个整数,表示最少需要的炸弹的数目。
Sample Input
Original Transformed
4
2
1
2
4
Sample Output
Original Transformed
2
分析:此处每个房间是独立的,即1号房间与2号不是相邻的,可以选择先进1号房间或其他房间,没顺序约束。此时,题目即转化为简单的并查集,即输入的整数a与房间序号i是连通的,虽然看似只能依序才能进入房间,但不会影响每个房间链(即连通图)最少只要一颗炸弹,所以其实只需计算独立的连通图的个数即可。(具体格式介绍见连通图)
#include<cstdio>
#include<algorithm>
using namespace std;
const int maxn = 1000010;
int p[maxn];
int sum;
int Find(int x){//查找
return p[x] == x ? x : p[x] = Find(p[x]);
}
void Union(int x, int y){//合并
int x_root = Find(x);
int y_root = Find(y);
if(x_root != y_root){
p[x_root] = y_root;
sum--;//成功的合并,总体未连通点的数目减1
}
}
int main(){
int a, n;
scanf("%d", &n);
sum = n;
for(int i = 1; i <= n; i++) p[i] = i;//初始化
for(int i = 1; i <= n; i++){
scanf("%d", &a);
Union(a, i);//合并
}
printf("%d\n", sum);
return 0;
}