题意:给出n个房间。每个房间上都有锁。每个房间内有一定数量的能开其他房间的钥匙,若当前手里的钥匙不能再打开新的房间,我们可以选择用炸弹炸开一个新的房间,给出每个房间中钥匙的数目和具体能开那个房间,求要打开所有房间,需要使用的炸弹数目。
思路:单独考虑每个点打开需要用炸弹的期望次数,那么所有点的期望之和就是答案。每个点 v 需要用炸弹的期望次数是 1/S, S是u(u->v连通)的数量,然后就变成求图中多少点能到达图中任意一个点了,这就是要求这个图的传递闭包了,普通的floyd显然会tle,新get的技巧是用bitset优化。至于期望为什么是1/S,因为对应能打开该房间的这S个房间里面必定要用一个炸弹砸开,而砸v的概率为1/S。
代码:
#include<bits/stdc++.h>
#define MAXN 1001
using namespace std;
int n;
bitset<MAXN> mp[MAXN];
double floyd()
{
double ans = 0;
int cnt = 0;
for(int i = 1; i <= n; i++)
for(int j = 1; j <= n; j++)
if(mp[j][i]) mp[j] |= mp[i];
for(int i = 1; i <= n; i++)
{
cnt = 0;
for(int j = 1; j <= n; j++)
if(mp[j][i]) cnt++;
ans += 1.0 / cnt;
}
return ans;
}
int main()
{
int T, k, u, kase = 1;
cin >> T;
while(T--)
{
scanf("%d", &n);
for(int i = 1; i <= n; i++) mp[i].reset();
for(int i = 1; i <= n; i++)
{
scanf("%d", &k);
while(k--){
scanf("%d", &u);
mp[i][u] = 1;
}
mp[i][i] = 1;
}
printf("Case #%d: %.5lf\n", kase++, floyd());
}
return 0;
}