抓小偷(强连通)
第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;
}

浙公网安备 33010602011771号