【复赛模拟试题】指认坏人 基环外向树+最大独立集

来源:互联网 发布:python3 apache 编辑:程序博客网 时间:2024/05/18 03:51

【问题描述】

  n个人中有好人,也有坏人。坏人知道每个人的身份,好人不知道。   现在每个人都指认了一个坏人(没有人会指认自己为坏人),当然,好人基本是乱猜的,而坏人则全部指认的是好人。   那么在不知道谁是坏人的情况下,最多可能有多少坏人。

【输入格式】

  输入的第1行包含一个整数n,表示有n个人。这n个人标号为1到n。接下来有n行,每行一个数,其中的第i行表示被第i个人指认的坏人编号。没有谁会指认自己为坏人。

【输出格式】

  仅输出一个整数,表示最多可能的坏人的数量。

【输入样例】

【样例1】
 3
 2
 1
 1

【样例2】
 3
 2
 3
 1

【样例3】
 7
 3
 3
 4
 5
 6
 4
 4

【输出样例】

【样例1】
 2

【样例2】
 1

【样例3】
 4

【样例解释】

第一个样例解释:杀手可能为2和3;
第二个样例解释:杀手可能是任何1个人,但不可能多于1个,否则杀手就会指认自己人了。

【数据范围】

40%的数据n<25.
80%的数据n<=2000
100%的数据 2<=N<=500000

——————————————————————————————————————————————

分析一下,可以发现这个图不具有连通性。
可以发现图上的边的方向并没有卵用,因为坏人是不可以相互指认的,所以问题变成了选择一个点的集合,使得从这个集合出发的指认不指向这个集合中的任意元素,求集合的元素最多有多少个,如果这是一颗树的话就可以直接做最大独立集了。但是这不一定是一棵树。不过由于每个点只发出一条边可以证明这是一颗基环外向树。然后问题就简单了,找到环上的一条边,dp的时候不经过这条边(我用的dp),同时每一次dp之前要确定这条边的哪边是好人。然后就可以dp了。
再说明一个问题,代码中实际上如果这个环上只有两个点的话找不到这条边就算了,完全没有影响,充其量就是多dp了一次而已。

AC代码:

#include<iostream>#include<cstdio>#include<cstring>#include<cstdlib>#include<algorithm>#include<cmath>#include<queue>#include<set>#include<map>#include<vector>#include<cctype>using namespace std;typedef long long LL;const int maxn=500005;int N;struct edge{ int to,next,id; }E[maxn<<1];int first[maxn],np,ID,s,t;int f[maxn][2],ans;bool vis[maxn];void _scanf(int &x){    x=0;    char ch=getchar();    while(ch<'0'||ch>'9') ch=getchar();    while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();}void add_edge(int u,int v,int id){    E[++np]=(edge){v,first[u],id};    first[u]=np;}void data_in(){    _scanf(N);    int x;    for(int i=1;i<=N;i++)    {        _scanf(x);        add_edge(x,i,i);        add_edge(i,x,i);    }}void DFS(int i,int fa){    vis[i]=1;    for(int p=first[i];p;p=E[p].next)    {        int j=E[p].to;        if(vis[j])        {            if(j!=fa) ID=E[p].id,s=i,t=E[p].to;            continue;        }        DFS(j,i);    }}void DFS(int i,int fa,int nc){    f[i][0]=f[i][1]=0;    if(i!=nc) f[i][1]=1;    for(int p=first[i];p;p=E[p].next)    {        if(ID==E[p].id) continue;        int j=E[p].to;        if(j==fa) continue;        DFS(j,i,nc);        f[i][0]+=max(f[j][0],f[j][1]);        if(i!=nc) f[i][1]+=f[j][0];    }}void work(){    for(int i=1;i<=N;i++) if(!vis[i])    {        DFS(i,0);        DFS(i,0,s);        int tmp=max(f[i][0],f[i][1]);        DFS(i,0,t);        tmp=max(tmp,max(f[i][0],f[i][1]));        ans+=tmp;    }    printf("%d\n",ans);}int main(){    freopen("badperson.in","r",stdin);    freopen("badperson.out","w",stdout);    data_in();    work();    return 0;}
原创粉丝点击