杀人游戏

题目描述
杀人游戏是一个在信息奥赛选手中流行的游戏,这个游戏不在结果,重在参与,就像信息奥赛一样。这个游戏的一方称为杀手,剩下的一方称为平民。杀手知道每个人的身份,但是平民不知道。在游戏过程中,平民的任务是找出谁是杀手。
游戏玩了若干轮,现在还剩下n个人,每个人都指认了一个杀手。,当然,平民基本是乱猜的,而杀手则全部指认的是平民。在不知道谁是杀手的情况下,最多可能有多少杀手。

输入
输入格式:
第一行包含一个整数N(2<=N<=500000),表示还有n个人。这n个人标号为1到n。
接下来有n行,每行一个数,其中的第k行表示被第k个人指认为杀手的人。
没有谁会指认自己为杀手。
输出
一个整数,表示最多可能的杀手的数量。

样例输入
3
2
1
1

样例输出
2

一个伪森林。由于一个点只能指向另一个点,所以一个连通分量里最多有一个环。
有一个贪心算法,对于每个连通分量,找到所有入度为0的点,视为杀手,它们所指向的点就为平民。
删掉杀手点和平民点,其他点的入度相应改变。再重复这个操作多次。  
最后只会剩下一个环。将环中的任一点视为平民(这并不影响),然后删掉这个点,环变成链,继续上面的操作就可以了。
#include<cstdio>
#define MAXN 500000

struct node{
    int v;
    node *next;
}edge[MAXN+10],*adj[MAXN+10],*ecnt=&edge[0];

int n,inp[MAXN+10],son[MAXN+10],ans;
bool vis[MAXN+10];
void read()
{
    int x;
    scanf("%d",&n);
    for(int i=1;i<=n;i++){
        scanf("%d",&x);
        son[i]=x;
        inp[x]++;
    }
}
void dfs(int u,int d)
{
    if(vis[u]) return ;
    vis[u]=true;
    ans+=d;
    if(--inp[son[u]]==0 || d==1)
        dfs(son[u],1-d);
}
void workout()
{
    for(int i=1;i<=n;i++)
        if(!inp[i])
            dfs(i,1);
    for(int i=1;i<=n;i++)
        dfs(i,0);
    printf("%d\n",ans);
}
int main()
{
    read();
    workout();
    return 0;
}
posted @ 2015-09-15 20:32  KatarinaYuan  阅读(145)  评论(0编辑  收藏  举报