杀人游戏
问题 C: [中山市选2011]杀人游戏
题目描述
一位冷血的杀手潜入 Na-wiat,并假装成平民。警察希望能在 N 个人里面,
查出谁是杀手。
警察能够对每一个人进行查证,假如查证的对象是平民,他会告诉警察,他
认识的人, 谁是杀手, 谁是平民。 假如查证的对象是杀手, 杀手将会把警察干掉。
现在警察掌握了每一个人认识谁。
每一个人都有可能是杀手,可看作他们是杀手的概率是相同的。
问:根据最优的情况,保证警察自身安全并知道谁是杀手的概率最大是多
少?
输入
第一行有两个整数 N,M。
接下来有 M 行,每行两个整数 x,y,表示 x 认识 y(y 不一定认识 x) 。
输出
仅包含一行一个实数,保留小数点后面 6 位,表示最大概率。
样例输入
5 4
1 2
1 3
1 4
1 5
样例输出
0.800000
杀人游戏:
比较难的是怎么想出来如何计算概率:
警察在搜寻的过程中只有两种结果——找到犯人或死亡,而死亡意味着你找的人是罪犯,因为每个人是罪犯的概率是相等的,所以问题转化为了要调查尽可能少的人来确定所有人的身份,注意到这是一个有向图,所以对那些强连通分量进行tarjan缩点处理,因为只要调查了他们中的一个人,那么就能知道其中所有人的身份,缩点完后在新建的图中是一个森林,我们要找的是那些入度为0的点,结果就是点的数量/总点数,入度不为0那一定能通过其他的点到达这里;
注意到会有一种特殊情况:如果有一个点没有入度也没有出度(初始时),那么只要调查了其他所有人都不是罪犯,则警察可以不用再去调查他了,他一定罪犯,所以在新图中进行判断,如果入度出度均为0且缩点后点的大小为0且其所能到达的点中只有一个初度,就对点的数量-1/sum就是结果了。
代码:
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 #include<vector> 6 using namespace std; 7 vector<int>v[100001]; 8 int n,m,x,y,num,cnt,dex; 9 int adj[100001],in[100001],out[100001]; 10 int dfn[100001],low[100001],belong[100001]; 11 struct edge{ 12 int s,t,next; 13 }k[300001],a[300001]; 14 int read(){ 15 int sum=0;char ch=getchar(); 16 while(ch<'0'||ch>'9') ch=getchar(); 17 while(ch>='0'&&ch<='9'){sum=sum*10+ch-'0';ch=getchar();} 18 return sum; 19 } 20 void init(int x,int y){ 21 k[num].s=x;k[num].t=y; 22 k[num].next=adj[x];adj[x]=num++; 23 } 24 bool t[100001];int zhan[100001],size[100001],head; 25 void tarjan(int x){ 26 dfn[x]=low[x]=++dex; 27 t[x]=1;zhan[++head]=x; 28 for(int i=adj[x];i!=-1;i=k[i].next){ 29 int o=k[i].t; 30 if(dfn[o]==-1){ 31 tarjan(o); 32 low[x]=min(low[x],low[o]); 33 } 34 else 35 if(t[o]) 36 low[x]=min(low[x],dfn[o]); 37 } 38 if(low[x]==dfn[x]){ 39 int temp;cnt++; 40 while(1){ 41 temp=zhan[head--]; 42 belong[temp]=cnt; 43 t[temp]=0;size[cnt]++; 44 if(temp==x) 45 break; 46 } 47 } 48 } 49 bool search(int x){ 50 for(int i=0;i<v[x].size();++i) 51 if(in[v[x][i]]==1) 52 return false; 53 return true; 54 } 55 int main(){ 56 // freopen("killer.in","r",stdin); 57 // freopen("killer.out","w",stdout); 58 memset(adj,-1,sizeof(adj)); 59 memset(dfn,-1,sizeof(dfn)); 60 n=read();m=read(); 61 for(int i=1;i<=m;++i){ 62 x=read();y=read(); 63 init(x,y); 64 } 65 for(int i=1;i<=n;++i) 66 if(dfn[i]==-1) 67 tarjan(i); 68 for(int i=0;i<num;++i){ 69 if(belong[k[i].t]!=belong[k[i].s]){ 70 in[belong[k[i].t]]++; 71 out[belong[k[i].s]]++; 72 v[belong[k[i].s]].push_back(belong[k[i].t]); 73 } 74 } 75 int ans=0;bool flag=false; 76 for(int i=1;i<=cnt;++i){ 77 if(!in[i]) 78 ans++; 79 if((!in[i]&&!out[i]&&size[i]==1)||(!flag&&!in[i]&&search(i)&&size[i]==1)) 80 flag=true; 81 } 82 if(flag) ans--; 83 double res=1-(double)ans/n; 84 printf("%.6lf",res); 85 //while(1); 86 return 0; 87 }