杀人游戏
问题 p: 杀人游戏
时间限制: 1 Sec 内存限制: 128 MB提交: 3 解决: 2
[提交] [状态] [讨论版] [命题人:admin]
题目描述
一位冷血的杀手潜入 Na-wiat,并假装成平民。警察希望能在 N 个人里面,查出谁是杀手。警察能够对每一个人进行查证,假如查证的对象是平民,他会告诉警察,他认识的人, 谁是杀手, 谁是平民。 假如查证的对象是杀手, 杀手将会把警察干掉。现在警察掌握了每一个人认识谁。每一个人都有可能是杀手,可看作他们是杀手的概率是相同的。问:根据最优的情况,保证警察自身安全并知道谁是杀手的概率最大是多少?
输入
第一行有两个整数 N,M。
接下来有 M 行,每行两个整数 x,y,表示 x 认识 y(y 不一定认识 x) 。
接下来有 M 行,每行两个整数 x,y,表示 x 认识 y(y 不一定认识 x) 。
输出
仅包含一行一个实数,保留小数点后面 6 位,表示最大概率。
样例输入
5 4
1 2
1 3
1 4
1 5
样例输出
0.800000
提示
警察只需要查证 1。假如1是杀手,警察就会被杀。假如 1不是杀手,他会告诉警察 2,3,4,5 谁是杀手。而 1 是杀手的概率是 0.2,所以能知道谁是杀手但没被杀的概率是0.8。对于 100%的数据有 1≤N ≤ 10 0000,0≤M ≤ 30 0000
Tarjan缩点后求强连通,有x个入度为0的点,则需要调查x次,则最后安全几率为1-x/n。
但这样不完全对,存在一种情况,当警察调查完x-1个人之后,他可以直接确定最后一个人是不是罪犯。这样安全的几率为1-(x-1)/n。而其充分条件为该入度为0的强连通分量,其相连的其他强连通分量入度大于1,。
#include <bits/stdc++.h> #define maxn 200000 using namespace std; int ind[maxn],low[maxn],dfn[maxn],in[maxn],out[maxn],tot,cnt; bool vis[maxn]; vector<int> a[maxn]; stack<int> s; struct Edge { int v,next; }; struct M { int head[maxn]; Edge edge[maxn*3]; int cnt; void init() { memset(head,-1, sizeof(head)); cnt=0; } void addedge(int u,int v) { edge[cnt].v=v; edge[cnt].next=head[u]; head[u]=cnt++; } }Mp; void Tarjan(int num) { vis[num]=true; low[num]=dfn[num]=++tot; s.push(num); for(int i=Mp.head[num];i!=-1;i=Mp.edge[i].next) { int v=Mp.edge[i].v; if(!dfn[v]) { Tarjan(v); low[num]=min(low[num],low[v]); } else if(vis[v]) { low[num]=min(low[num],dfn[v]); } } if(dfn[num]==low[num]) { ++cnt; while(true) { int now=s.top(); a[cnt].push_back(now); s.pop(); vis[now]=false; ind[now]=cnt; if(now==num) break; } } } int main() { //freopen("in.txt","r",stdin); int n,m,u,v; scanf("%d%d",&n,&m); Mp.init(); for(int i=1;i<=m;i++) { scanf("%d%d",&u,&v); Mp.addedge(u,v); } for(int i=1;i<=n;i++) { if(!dfn[i]) Tarjan(i); } for(int i=1;i<=n;i++) { for(int j=Mp.head[i];j!=-1;j=Mp.edge[j].next) { int v=Mp.edge[j].v; if(ind[v]!=ind[i]) { in[ind[v]]++; out[ind[i]]++; } } } int sum=0; for(int i=1;i<=cnt;i++) { if(in[i]==0) sum++; } double ans; for(int i=1;i<=cnt;i++) { if(in[i]==0&&a[i].size()==1) { int u=a[i][0]; bool f=false; for(int j=Mp.head[u];j!=-1;j=Mp.edge[j].next) { int v=Mp.edge[j].v; if(in[ind[v]]<=1) f=true; } if(f==false) { sum--; break; } } } ans=1-1.0*sum/n; printf("%.6lf\n",ans); }