hdu 5036 Explosion||2014 北京区域赛网络赛 (概率+bitset优化的状态压缩+floyd传递闭包)
题意:有n扇门,n种钥匙,一一对应。每扇门打开后可能得到k把钥匙(k可能为0)。一扇门还可以用一颗炸弹炸开。现在问要开所有门,使用炸弹的期望个数。
思路:状态压缩。用一个二进制串表示每扇门能打开的门的信息,对应的位上为1表示能打开,为0表示不能打开。
状态是可以传递的。。
如果第i扇门能打开门k,那么能打开第i扇门的第j扇门也可以打开门k。
状态压缩以及传递的过程可以很容易用bitset来维护,这才是bitset的正确打开姿势
相当于用floyd做了一个传递闭包。(floyd的有一层循环隐藏在了bitset中,复杂度没有改变,但是常数小)
最后对于期望的计算方法:统计能打开第i扇门的方案数计为cnt,这cnt的方案中,只有一种是用炸弹炸掉,因此用的炸弹数的期望数为1/cnt
由于期望的独立性,因此打开所有门所有的炸弹数的期望就是每个门的期望累加。
1/* ***********************************************
2Author :111qqz
3Created Time :2016年08月21日 星期日 18时43分56秒
4File Name :code/hdu/5036.cpp
5************************************************ */
6#include <cstdio>
7#include <cstring>
8#include <iostream>
9#include <algorithm>
10#include <vector>
11#include <queue>
12#include <set>
13#include <deque>
14#include <map>
15#include <string>
16#include <cmath>
17#include <cstdlib>
18#include <bitset>
19#define fst first
20#define sec second
21#define lson l,m,rt<<1
22#define rson m+1,r,rt<<1|1
23#define ms(a,x) memset(a,x,sizeof(a))
24typedef long long LL;
25#define pi pair < int ,int >
26#define MP make_pair
27using namespace std;
28const double eps = 1E-8;
29const int dx4[4]={1,0,0,-1};
30const int dy4[4]={0,-1,1,0};
31const int inf = 0x3f3f3f3f;
32const int N=1005;
33int n;
34bitset<N>b[N]; //b[i]表示第i扇门可以打开的门
35int main()
36{
37 #ifndef ONLINE_JUDGE
38 freopen("code/in.txt","r",stdin);
39 #endif
40 int T;
41 cin>>T;
42 int cas = 0 ;
43 while (T--)
44 {
45 printf("Case #%d: ",++cas);
46 scanf("%d",&n);
47 for ( int i = 0 ; i <= n ; i++)
48 {
49 b[i].reset();
50 b[i].set(i); //第i扇门可以炸开自己。。。
51 }
52 for ( int i = 0 ; i < n; i++)
53 {
54 int num;
55 scanf("%d",&num);
56 while (num--)
57 {
58 int x;
59 scanf("%d",&x);
60 x--;
61 b[i].set(x);
62 }
63 }
64 for ( int i = 0 ; i < n ; i++) //枚举能打开第i扇门的门j,因为能开的门可以传递,所以门j可以通过打先开门i去开门i能开的门。
65 for ( int j = 0 ; j < n ; j++)
66 if (b[j][i])
67 b[j]|=b[i]; //依然是floyd,这里或运算的复杂度是n,只不过常数小。相当于做了一个传递闭包。
68 double ans = 0 ;
69 for ( int i = 0 ; i < n ; i++) //统计能打开门i的方案数为cnt(cnt至少为1),那么cnt中一种是炸开门,剩下cnt-1种是其他方式开门。
70 { //因此第i扇门用炸弹炸开的期望就是1/cnt. 由于期望的独立性。答案把所有门累加即可。
71 int cnt = 0 ;
72 for ( int j = 0 ; j < n ; j++)
73 if (b[j][i]) cnt++;
74 ans +=1.0/cnt;
75 }
76 printf("%.5f\n",ans);
77 }
78 #ifndef ONLINE_JUDGE
79 fclose(stdin);
80 #endif
81 return 0;
82}