蓝桥杯选拔F
设有一个由 n 个人组成的社团。社团中的每个成员给社团中的一个成员发送一条短信。如果有一个由社团成员组成的子社团,每个子社团中成员恰好收到一条由该子社团成员发送的短信,则称这个子社团为一个和谐子社团。和谐短信问题要求社团的最大和谐子社团。例如,设有一个由 7 个人组成的社团。将社团中的成员编号为:0,1,…,6。他们分别发送一条短信给社团成员:1,0,0,2,2,3,6。则容易看出社团成员 0,1,6 构成一个最大和谐子社团。
Input
★算法设计: 对于给定的由 n 个人组成的社团以及社团员间发送短信的信息,设计一个计算最大和谐子社团的算法。
对于每组输入数据,输入数据的第 1 行有 1 个正整数 n (1<=n<=600,000),表示社团由 n 个人组成。将社团中的成员编号为:0,1,…,n-1。第 i 个人发送一条短信给社团中成员 f (i) ,0 <= i < n ,0 <= f (i) < n 。文件的第 2 行是 f (i) 的值, 0 <= i < n 。
Output
输出计算出的社团的最大和谐子社团大小。
Sample Input
7 1 0 0 2 2 3 6Sample Output
3
很容易发现是要选出最多的点使得每个点的入度都是1;然后就想到了以下算法:
1.把入度为0的点去掉(入度只有可能减少,不可能增加),同时把与这个点的相连的线去掉;
2.重复1,直到没有入度为0的点;
3.剩下的点集就是最大和谐子社团(入度为0的点都去掉了,所有所有点的入度比然大于等于1,又因为出度为1,所以剩下的点入度都为1)。
但是写的时候发现写不出(可能是STL的优先队列没学好,要是哪位大佬会用优先队列实现代码借在下观摩一下),然后就换了一种方法,反过来写:
1.记录入度变为0的点;
2.将入度为0的点相连的线去掉;
3重复1、2,直到没有变成入度为0的点;
#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include<queue>
#include<vector>
using namespace std;
typedef long long LL;
const double PI = acos(-1.0);
const double eps = 1e-6 ;
const int INF = 1e9 ;
const int maxn = 600000+10 ;
const int mod = 1000000;
int to[maxn],getCount[maxn];
int que[maxn];
void inti(int n)
{
int i;
for( i=0;i<=n;i++)
getCount[i]=0;
}
int main()
{
int n;
int i,first,last,t,ans;
while(~scanf("%d",&n))
{
inti(n);
for( i=0;i<n;i++)
{
scanf("%d",&to[i]);
getCount[to[i]]++;
}
first=0,last=0;
for( i=0;i<n;i++)
{
if(getCount[i]==0)
que[last++]=i;
}
while(first!=last)
{
t=que[first];
first++;
getCount[to[t]]--;
if(getCount[to[t]]==0)
que[last++]=to[t];
}
ans=0;
for( i=0;i<n;i++)
{
if(getCount[i])
ans++;
}
printf("%d\n",ans);
}
return 0;
}