抓小偷(强连通)
第4题 抓小偷 查看测评数据信息
你是一名警察,现在有一个案子交给你,N个人里面其中有一人是小偷,你的任务是查出来谁是小偷,你可以一个一个的盘问,假如盘问的对象不是小偷,他会告诉你,他认识谁,谁是好人,谁是小偷,假如你盘问的对象是小偷,小偷就会袭警,现在你掌握了每一个人认识谁。每一个人都有可能是小偷,可看作他们是小偷的概率是相同的。
问:根据最优的情况,保证警察自身安全并知道谁是小偷的概率最大是多少?
输入格式
第一行有两个整数 N,M。 对于100的数据有1≤N≤100000,0≤M≤300000。
接下来有 M 行,每行两个整数 x,y,表示 x 认识 y(y 不一定认识 x)。
输出格式
仅包含一行一个实数,保留小数点后面 6 位,表示最大概率。
输入/输出例子1
输入:
5 4
1 2
1 3
1 4
1 5
输出:
0.800000
样例解释
警察只需要查证1。假如1是小偷,警察就会被杀。假如1不是小偷,他会告诉警察2,3,4,5谁是小偷。而1是小偷的概率是0.2,所以能知道谁是小偷但没被杀的概率是0.8。
关键在于用缩点把图变成有向无环图
找需要查证的人即可,答案就是(总人数-需查证人数)/ 总人数
注意分类别漏了
先缩点,变有向无环图,剩下的就全是链了
1.链:搞入度为0的点,只需要问入度为0的点对应的那个人,剩下的人都可以被推出来
2.环:搞环中任何一个人 (情况排除了,因为缩点后不存在环)
3.判断入度为0的点可否用排除法,设排除u
1)size[u]==1,不能是缩点后的一个胖点,排除法只能排除一个人
2)u的所有儿子son入度>1,不然你排除了u点,他的儿子身份也无法确定
4.图只有一个点,直接100%,因为他只能是小偷
#include <bits/stdc++.h> using namespace std; const int N=600005; int n, m, u1, v1, dfn[N], low[N], idx=0, cnt=0, id[N], rd[N], ans=0, size[N]; bool vis[N], flag=0; stack<int> st; vector<int> a[N], b[N]; void dfs(int u) { dfn[u]=low[u]=++idx; st.push(u); for (int i=0; i<a[u].size(); i++) { int v=a[u][i]; if (!dfn[v]) { dfs(v); low[u]=min(low[u], low[v]); } else if (!id[v]) low[u]=min(low[u], dfn[v]); } if (dfn[u]==low[u]) { cnt++; while (st.top()!=u) { id[st.top()]=cnt; size[cnt]++; st.pop(); } id[st.top()]=cnt; size[cnt]++; st.pop(); } } bool check(int x) { if (rd[x]!=0 || size[x]>1) return false; for (int i=0; i<b[x].size(); i++) if (rd[b[x][i]]<=1) return false; return true; } int main() { scanf("%d%d", &n, &m); if (n==1) { printf("1.000000"); return 0; } for (int i=1; i<=m; i++) { scanf("%d%d", &u1, &v1); a[u1].push_back(v1); } for (int i=1; i<=n; i++) if (!dfn[i]) dfs(i); for (int u=1; u<=n; u++) { for (int j=0; j<a[u].size(); j++) { int v=a[u][j]; if (id[u]==id[v] || vis[id[v]]) continue; b[id[u]].push_back(id[v]); rd[id[v]]++, vis[id[v]]=1; } for (int j=0; j<a[u].size(); j++) vis[id[a[u][j]]]=0; } /*for (int u=1; u<=cnt; u++) { printf("%d : {", u); for (int j=0; j<b[u].size(); j++) { printf("%d ", b[u][j]); } printf(" }\n"); }*/ for (int i=1; i<=cnt; i++) if (!rd[i]) ans++; for (int i=1; i<=cnt; i++) if (check(i)) { ans--; break; } printf("%.6lf", (double)(n-ans)/n); return 0; }