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}