小猪存钱罐

Description
Byteazar有 N 个小猪存钱罐. 每个存钱罐只能用钥匙打开或者砸开. Byteazar已经把每个存钱罐的钥匙放到了某
些存钱罐里. Byteazar 现在想买一台汽车于是要把所有的钱都取出来. 他想尽量少的打破存钱罐取出所有的钱,问
最少要打破多少个存钱罐.
Input
第一行一个整数 N (1 <= N <= 1.000.000)表示存钱罐的总数.
接下来每行一个整数,第 i+1行的整数代表第i个存钱罐的钥匙放置的存钱罐编号.
Output
一个整数表示最少打破多少个存钱罐.
Sample Input
4
2
1
2
4
Sample Output
2

sol:两种方法:1.强连通分量缩圈;2.并查集
方法一中样例用有向图表示为:

缩圈后有两个入度为0的点,所以答案为2.

缩圈写法:

 1 #include<bits/stdc++.h>
 2 #define ll long long
 3 #define N 1000005
 4 int n,w,head[N],nxt[N],ans,size[N];
 5 int to[N],cnt,dfn[N],st[N],r,k[N];
 6 int low[N],tot,top,num[N];
 7 bool vis[N];
 8 using namespace std;
 9 inline void read(int &x){
10     int datta=0;char chchc=getchar();bool okoko=0;
11     while(chchc<'0'||chchc>'9'){if(chchc=='-')okoko=1;chchc=getchar();}
12     while(chchc>='0'&&chchc<='9'){datta=datta*10+chchc-'0';chchc=getchar();}
13     x=okoko?-datta:datta;
14 }
15 inline void link(int a,int b){to[++cnt]=b,nxt[cnt]=head[a],head[a]=cnt;}
16 inline void t(int x){
17     dfn[x]=low[x]=++tot;
18     st[++top]=x;int ttt=top;
19     for(int i=head[x];i;i=nxt[i]){
20         int u=to[i];
21         if(!num[u]){
22             if(!dfn[u])t(u);
23             low[x]=min(low[u],low[x]);
24         }
25     }
26     if(dfn[x]==low[x]){
27         r++;
28         while(top>=ttt)
29             num[st[top--]]=r,size[r]++;
30     }
31 }
32 int main(){
33     read(n);
34     for(int i=1;i<=n;i++)
35         read(w),link(w,i);
36     for(int i=1;i<=n;i++)
37         if(!dfn[i])t(i);
38    for(int i=1;i<=n;i++)
39         for(int j=head[i];j;j=nxt[j])
40             if(num[to[j]]!=num[i])k[num[to[j]]]++;
41     for(int i=1;i<=r;i++)
42         if(!k[i])ans++;
43     printf("%d
",ans); 
44     return 0;
45 }

并查集写法:

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 int fa[1000001],n,ans;
 4 bool flag[1000001];
 5 int get(int x){
 6     if(fa[x]!=x)fa[x]=get(fa[x]);
 7     return fa[x];
 8 }
 9 int main() {
10     scanf("%d",&n);
11     for(int i=1;i<=n;i++){
12         fa[i]=i;
13     }
14     for(int i=1;i<=n;i++)
15     {
16         int x;
17         scanf("%d",&x); 
18         //第x个box的KEY在第i个box里,将它们并成一个集合
19         int p=get(x),q=get(i);
20         if(p!=q)
21         {
22             fa[p]=q;
23         }
24     }
25     for(int i=1;i<=n;i++)
26     {
27         int x=get(i);
28         if(fa[i]==i) //统计有多少个集合,就有打开多少个box
29         {
30             ans++;
31             flag[x]=true;
32         }
33     }
34     printf("%d",ans);
35     return 0;
36 }
原文地址:https://www.cnblogs.com/cutepota/p/12747761.html