• CF482C Game with Strings


    洛谷传送门 CF传送门

    Solution

    观察发现 (mleq 20) ,那么我们可以想到状压DP。

    将位置压起来,设 (f_i) 表示此时状态为 (i) ,距离确定一个字符串所需要的期望,那么可得:

    [f_i=sum_{!(iAnd(1<<j)}frac {f_{i|(1<<j)}}{tot}+1 ]

    其中 (tot) 表示状态 (i) 中还有几个位置没问(也就是还有几个 (0) ),那因为询问剩余位置的概率相同,所以此方程是对的。

    但是每次询问不一定就能唯一一个字符串,可能是好几个,所以需要处理出来对于每一个状态能确定几个字符串。

    那么设 (num_i) 表示符合 (i) 这个状态的字符串的数量,可得:

    [f_i=sum_{!(iAnd(1<<j)}frac {f_{i|(1<<j)}}{tot} imes frac {num_{i|(1<<j)}}{num_i}+1 ]

    详细解释一下 (dfrac {num_{i|(1<<j)}}{num_i}) :首先设 (k=i|(1<<j)) ,那么 (num_kleq num_i) ,因为 (k) 多了一位确定的。而 (i)(num_k) 种可能变成 (k) ,剩下的 (num_i-num_k) 是直接得到唯一确定字符串的,所以继续转移的可能就是 (dfrac {num_{i|(1<<j)}}{num_i})

    好,转移方程得到了,怎么求得 (num) 数组呢?

    这个时候,就要再来一个辅助的东西——设 (unf_i) 表示状态为 (i) 的情况下,哪些字符串是符合的,或者说是不能直接确定的(这里是拿二进制状态存串是否满足条件),因为在 (i) 这个状态下,某几个串表达的东西可能一样。

    对于 (unf) 数组,直接 (O(n^2)) 暴力匹配即可,然后从大到小更新。

    那么 (num_i) 就是 (unf_i)(1) 的数量了。

    注意:代码中状压时枚举要往后错一位。

    Code

    #include<bits/stdc++.h>
    #define int long long
    
    using namespace std;
    const int N=(1ll<<20)+10,M=55;
    int m,n,unf[N],num[N];
    char s[M][M];
    double f[N];
    
    signed main(){
        scanf("%lld",&n);
        for(int i=1;i<=n;i++) scanf("%s",s[i]+1);
        m=strlen(s[1]+1);
        for(int i=1;i<=n;i++)
            for(int j=i+1;j<=n;j++){
                int tmp=0;
                for(int k=1;k<=m;k++)
                    if(s[i][k]==s[j][k]) tmp|=(1ll<<(k-1));
                unf[tmp]|=(1ll<<(i-1)); unf[tmp]|=(1ll<<(j-1));
            }
        for(int i=(1ll<<m)-1;i>=1;i--)
            for(int j=1;j<=m;j++)
                if(i&(1ll<<(j-1))) unf[i^(1ll<<(j-1))]|=unf[i];
        for(int i=0;i<(1ll<<m);i++)
            for(int j=1;j<=n;j++)
                if(unf[i]&(1ll<<(j-1))) num[i]++;
        for(int i=(1ll<<m)-2;i>=0;i--){
            if(!num[i]) continue;
            int tot=m;
            for(int j=1;j<=m;j++)
                if(i&(1ll<<(j-1))) --tot;
            for(int j=1;j<=m;j++){
                if(i&(1ll<<(j-1))) continue;
                f[i]+=
                f[i|(1ll<<(j-1))]/(double)tot*((double)num[i|(1ll<<(j-1))]/(double)num[i]);
            }
            f[i]+=1.0;
        }
        printf("%.10lf
    ",f[0]);
        return 0;
    }
    
  • 相关阅读:
    嵌入式软件开发环境搭建与工具推荐
    软件常用设计原则与实践:契约式接口设计、安全编码实践
    服务器崩掉 ,只剩下挂载文件,重现github docker 容器
    基础巩固篇 —— CountDownLatch的理解
    基础巩固篇 —— 对锁的理解
    基础巩固篇 —— 对不安全集合的理解
    基础巩固篇 —— 对CAS的理解
    基础巩固篇 —— 对volatile关键字的理解
    响应式布局
    jQuery Mobile 网格
  • 原文地址:https://www.cnblogs.com/jasony/p/14049933.html
Copyright © 2020-2023  润新知