题目描述
一位冷血的杀手潜入Na-wiat,并假装成平民。警察希望能在NNN个人里面,查出谁是杀手。警察能够对每一个人进行查证,假如查证的对象是平民,他会告诉警察,他认识的人,谁是杀手,谁是平民。
假如查证的对象是杀手,杀手将会把警察干掉。
现在警察掌握了每一个人认识谁。每一个人都有可能是杀手,可看作他们是杀手的概率是相同的。
问:根据最优的情况,保证警察自身安全并知道谁是杀手的概率最大是多少?
对于100%100\%100%的数据有1≤N≤100000,0≤M≤3000001≤N≤100000,0≤M≤3000001≤N≤100000,0≤M≤300000。
题解:
这个题理解错题意了----
不是先问一个人,他告诉你一些人的身份,然后根据结果再决定。
没那么麻烦!~~~~
其实是你要一次性确定所有要问的人,问完他们必须要确定所有人的身份。
为了尽可能的活,必须问最少的人。
于是,题目就是,问最少的人,知道所有人的身份。
显然问入度为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; }