hdu 5036 Explosion||2014 北京区域赛网络赛 (概率+bitset优化的状态压缩+floyd传递闭包)

Posted by 111qqz on Sunday, August 21, 2016

TOC

题目链接

题意:有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;
}

「真诚赞赏,手留余香」

111qqz的小窝

真诚赞赏,手留余香

使用微信扫描二维码完成支付