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

由于期望的独立性,因此打开所有门所有的炸弹数的期望就是每个门的期望累加。

/* ***********************************************
Author :111qqz
Created Time :2016年08月21日 星期日 18时43分56秒
File Name :code/hdu/5036.cpp
************************************************ */
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <vector>
#include <queue>
#include <set>
#include <deque>
#include <map>
#include <string>
#include <cmath>
#include <cstdlib>
#include <bitset>
#define fst first
#define sec second
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define ms(a,x) memset(a,x,sizeof(a))
typedef long long LL;
#define pi pair < int ,int >
#define MP make_pair
using namespace std;
const double eps = 1E-8;
const int dx4[4]={1,0,0,-1};
const int dy4[4]={0,-1,1,0};
const int inf = 0x3f3f3f3f;
const int N=1005;
int n;
bitset<N>b[N]; //b[i]表示第i扇门可以打开的门
int main()
{
	#ifndef  ONLINE_JUDGE 
	freopen("code/in.txt","r",stdin);
  #endif
	int T;
	cin>>T;
	int cas = 0 ;
	while (T--)
	{
	    printf("Case #%d: ",++cas);
	    scanf("%d",&n);
	    for ( int i  = 0 ; i <= n ; i++)
	    {
		b[i].reset();
		b[i].set(i);  //第i扇门可以炸开自己。。。
	    }
	    for ( int i = 0 ; i < n; i++)
	    {
		int num;
		scanf("%d",&num);
		while (num--)
		{
		    int x;
		    scanf("%d",&x);
		    x--;
		    b[i].set(x);
		}
	    }
	    for ( int i = 0 ; i < n ; i++)  //枚举能打开第i扇门的门j,因为能开的门可以传递,所以门j可以通过打先开门i去开门i能开的门。
		for ( int j = 0 ; j < n ; j++)
		    if (b[j][i])
			b[j]|=b[i]; //依然是floyd,这里或运算的复杂度是n,只不过常数小。相当于做了一个传递闭包。
	    double ans = 0 ;
	    for ( int i = 0 ; i < n ; i++)  //统计能打开门i的方案数为cnt(cnt至少为1),那么cnt中一种是炸开门,剩下cnt-1种是其他方式开门。
	    {                               //因此第i扇门用炸弹炸开的期望就是1/cnt. 由于期望的独立性。答案把所有门累加即可。
		int cnt = 0 ;
		for ( int j = 0  ; j < n ; j++)
		    if (b[j][i]) cnt++;
		ans +=1.0/cnt;
	    }
	    printf("%.5f\n",ans);
	}
  #ifndef ONLINE_JUDGE  
  fclose(stdin);
  #endif
    return 0;
}