[中山市选]杀人游戏

题目描述

一位冷血的杀手潜入Na-wiat,并假装成平民。警察希望能在NNN个人里面,查出谁是杀手。警察能够对每一个人进行查证,假如查证的对象是平民,他会告诉警察,他认识的人,谁是杀手,谁是平民。

假如查证的对象是杀手,杀手将会把警察干掉。

现在警察掌握了每一个人认识谁。每一个人都有可能是杀手,可看作他们是杀手的概率是相同的。

问:根据最优的情况,保证警察自身安全并知道谁是杀手的概率最大是多少?

 

对于100%100\%100%的数据有1≤N≤100000,0≤M≤3000001≤N≤100000,0≤M≤3000001N100000,0M300000。

 

题解:

这个题理解错题意了----

不是先问一个人,他告诉你一些人的身份,然后根据结果再决定。

没那么麻烦!~~~~

其实是你要一次性确定所有要问的人,问完他们必须要确定所有人的身份。

为了尽可能的活,必须问最少的人。

于是,题目就是,问最少的人,知道所有人的身份。

显然问入度为0的,但是环也得问。

显然是tarjan缩点,无入度的环也得问。

但是,可能最后剩下一个人,可以排除法少问一个。

那就对于缩完不是环的点,如果这个点的所有出度点不只有1个度数(可以通过问别人知道),就可以不问他,但是这样的人只能有一个。

 

复杂度O(n+m)

代码:

#include<bits/stdc++.h>
using namespace std;
const int N=100000+5;
const int M=300000+6;
struct node{
    int nxt,to;
}e[M],bian[M];
int hd[N],cnt;
int n,m;
void add(int x,int y){
    e[++cnt].nxt=hd[x];
    e[cnt].to=y;
    hd[x]=cnt;
}
int dfn[N],low[N],tot,df;
int sz[N],c[N],dcc;
int pre[N],lol;
int d[N];
void add_c(int x,int y){
    bian[++lol].nxt=pre[x];
    bian[lol].to=y;
    pre[x]=lol;
}
int sta[N],top;
bool in[N];
void tarjan(int x){
    sta[++top]=x;
    low[x]=dfn[x]=++df;
    in[x]=1;
    for(int i=hd[x];i;i=e[i].nxt){
        int y=e[i].to;
        if(!dfn[y]){
            tarjan(y);
            low[x]=min(low[x],low[y]);
        }
        else if(in[y]) low[x]=min(low[x],dfn[y]);
    }
    if(low[x]==dfn[x]){
        dcc++;
        int z;
        do{
            z=sta[top];
            in[z]=0;top--;
            c[z]=dcc;
            sz[dcc]++;
        }while(z!=x);
    }
}
int main()
{
    scanf("%d%d",&n,&m);int x,y;
    for(int i=1;i<=m;i++){
        scanf("%d%d",&x,&y);add(x,y);
    }
    for(int i=1;i<=n;i++){
        if(!dfn[i]){
            tarjan(i);
        }
    }
    for(int x=1;x<=n;x++){
        for(int i=hd[x];i;i=e[i].nxt){
            int y=e[i].to;
            if(c[x]!=c[y]) {
            d[c[y]]++;
            add_c(c[x],c[y]);
            }
        }
    }
    bool fl=false;    
    for(int i=1;i<=dcc;i++){
        if(d[i]==0){
            if(sz[i]>1) tot++;
            else{
             if(!fl){
                bool exi=true;
                for(int j=pre[i];j;j=bian[j].nxt){
                    int k=bian[j].to;
                    if(d[k]==1) exi=false;
                }
                if(exi) fl=true;
                else tot++;
              }
             else tot++;
            }
        }
    }
    //cout<<tot<<endl;
    double op=((double)n-tot)/(double)n;
    printf("%.6lf",op);
    return 0;
    
}

 

posted @ 2018-09-06 21:49  *Miracle*  阅读(278)  评论(0编辑  收藏  举报