zoukankan      html  css  js  c++  java
  • 【bzoj5004】开锁魔法II 组合数学+概率dp

    题目描述

    有 $n$ 个箱子,每个箱子里有且仅有一把钥匙,每个箱子有且仅有一把钥匙可以将其打开。现在随机打开 $m$ 个箱子,求能够将所有箱子打开的概率。


    题解

    组合数学+概率dp

    题目约定了每个点的入度和出度均为1,因此最终的图一定是若干个环。每个环都至少选择一个点即可满足要求。

    预处理出每个环的点数 $c[i]$ 以及其后缀和 $sum[i]$ 。

    设 $f[i][j]$ 表示前 $i$ 个环中选出 $j$ 个点,满足最终条件的概率。初始化 $f[0][0]=1$ 。

    枚举 $i$ 和前 $i-1$ 个环的点数 $j$ 、第 $i$ 个环的点数 $k$ ,那么:$isim n$ 的总方案数为 $C_{sum[i]}^{m-j}$ ,满足条件的方案数为 $c[i]$ 中选出 $k$ 个的方案数乘以剩下部分选出 $m-j-k$ 个的方案数 $C_{c[i]}^k·C_{sum[i]-c[i]}^{m-j-k}$ 。

    整理一下即可得到dp方程 $f[i][j+k]leftarrow f[i-1][j]·frac{C_{c[i]}^k·C_{sum[i]-c[i]}^{m-j-k}}{C_{sum[i]}^{m-j}}$ 。

    最后的答案就是 $f[n][m]$ 。

    其中组合数直接使用double存据说能过,然而我比较怂,因此存的是阶乘的 $ln$ ,求的时候再 $ ext{exp}$ 回去。

    时间复杂度 $O(Tn^2)$

    #include <cmath>
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #define N 310
    using namespace std;
    int a[N] , c[N] , vis[N] , sum[N];
    double fac[N] , f[N][N];
    int main()
    {
        int T;
        scanf("%d" , &T);
        while(T -- )
        {
            memset(vis , 0 , sizeof(vis));
            memset(f , 0 , sizeof(f));
            f[0][0] = 1;
            int n , m = 0 , p , i , j , k;
            scanf("%d%d" , &n , &p);
            for(i = 1 ; i <= n ; i ++ ) scanf("%d" , &a[i]) , fac[i] = fac[i - 1] + log(i);
            for(i = 1 ; i <= n ; i ++ )
            {
                if(!vis[i])
                {
                    c[++m] = 0;
                    for(j = i ; !vis[j] ; j = a[j])
                        vis[j] = 1 , c[m] ++ ;
                }
            }
            sum[m + 1] = 0;
            for(i = m ; i ; i -- ) sum[i] = sum[i + 1] + c[i];
            for(i = 1 ; i <= m ; i ++ )
                for(j = max(i - 1 , p - sum[i]) ; j < p && j <= n - sum[i] ; j ++ )
                    for(k = 1 ; k <= c[i] && j + k <= p ; k ++ )
                        f[i][j + k] += f[i - 1][j] * exp(fac[c[i]] + fac[sum[i] - c[i]] + fac[p - j] + fac[sum[i] - p + j] - fac[k] - fac[c[i] - k] - fac[p - j - k] - fac[sum[i] - c[i] - p + j + k] - fac[sum[i]]);
            printf("%.9lf
    " , f[m][p]);
        }
        return 0;
    }
    
  • 相关阅读:
    人脸识别完整项目实战(1):目录大纲篇
    《分布式数据仓库最佳实践》学员答疑实录(2)
    知识图谱完整项目实战(附源码)(3)
    人脸识别完整项目实战(14):实时人脸特征点标定程序设计
    知识图谱完整项目实战(附源码)(2)
    sqlserver查询数据表中每个分类最新的一条记录
    WPF datagrid combobox 使用枚举
    中控考勤机开发-专业性门禁终端
    临时保存
    开源WPF控件库MaterialDesignInXAML推荐
  • 原文地址:https://www.cnblogs.com/GXZlegend/p/8724031.html
Copyright © 2011-2022 走看看