zoukankan      html  css  js  c++  java
  • CF482C Game with Strings (状压DP+期望DP)

    题目大意:甲和乙玩游戏,甲给出n(n<=50)个等长的字符串(len<=20),然后甲选出其中一个字符串,乙随机询问该字符串某一位的字符(不会重复询问一个位置),求乙能确定该串是哪个字符串的询问次数的期望值

    这题不看题解好难想......(感谢zhx和zhx两位大佬的题解)

    len很小,考虑状压DP,显然我们要状压询问,要定义两个状态,f[]和num[]

    1表示询问,0表示未询问

    那么,我们定义f[s]表示询问状态s距离确定一个字符串所需要的期望值。

     定义tot是s状态剩余的询问的次数,那么显然f[s]=(\sum f[s|(1<<i)]/tot)+1因为询问剩余的每一位的概率是相等的

    而一个询问并不一定能确定唯一一个字符串,可能是好几个,所以我们要处理这个询问状态能确定几个字符串,即num[s]

    而num[s]并不能通过暴力枚举得到,所以我们只能通过枚举它的父集来获取它的状态

    我们把字符串两两匹配,定义unidf[s]为s状态不能确定的串(用二进制表示哪些串不能确定),接着我们从大到小枚举状态,每个子集都从它的父集更新,取所有unidf[s|(1<<j)]的并集,其中1的数量即为num

    tip1:而如果1的数量为1,该状态仍然可以确定所有的串,所以此时num是0

    最后我们得到一个很玄学的方程

    f[s]=(\sum f[s|(1<<i)]/tot*num[s|(1<<i)]/num[s])+1

    最后的f[0]即为答案

    tip2:当n==1时,出题人貌似想告诉我们,因为不论问不问都只有一个串,所以不用问也知道是哪个......

     1 #include <cstdio>
     2 #include <algorithm>
     3 #include <cstring>
     4 #define N 55
     5 #define maxn (1<<20)+100
     6 #define ll long long 
     7 #define dd double
     8 #define seed 13131
     9 using namespace std;
    10 
    11 int n,m;
    12 char str[55][22];
    13 dd f[maxn];
    14 ll unidf[maxn];
    15 int num[maxn];
    16 
    17 int main()
    18 {
    19     //freopen("aa.in","r",stdin);
    20     scanf("%d",&n);
    21     if(n==1) {printf("0.0000000000\n");return 0;}
    22     for(int i=0;i<n;i++)
    23         scanf("%s",str[i]);
    24     m=strlen(str[1]);
    25     for(int i=0;i<n;i++)
    26         for(int j=i+1;j<n;j++)
    27         {
    28             int pos=0;
    29             for(int k=0;k<m;k++){
    30                 if(str[i][k]==str[j][k])
    31                    pos|=(1<<k);
    32             }
    33             unidf[pos]|=(1ll<<j)|(1ll<<i);
    34         }
    35     for(int s=(1<<m)-1;s>=0;s--)
    36     {
    37         for(int i=0;i<m;i++)
    38             if(!(s&(1<<i))) unidf[s]|=unidf[s|(1<<i)];
    39         for(int j=0;j<n;j++) 
    40             if(unidf[s]&(1ll<<j)) num[s]+=1;
    41         if(num[s]==1) num[s]=0;
    42     }
    43     f[(1<<m)-1]=0;
    44     for(int s=(1<<m)-2;s>=0;s--)
    45     {
    46         if(!num[s]) {f[s]=0;continue;}
    47         int tot=0;
    48         for(int i=0;i<m;i++) if(!(s&(1<<i)))tot++;
    49         for(int i=0;i<m;i++)
    50         {
    51             if(((1<<i)&s)) continue;
    52             f[s]+=(f[s|(1<<i)]/(dd)tot*(dd)num[s|(1<<i)]/(dd)num[s]); 
    53         }
    54         f[s]+=1.0;
    55     } 
    56     printf("%.10lf\n",max(f[0],1.00000000000));
    57     return 0;
    58 }
  • 相关阅读:
    Linux 系统中用户切换(su user与 su
    linux 用户打开进程数和文件数调整
    hive sql 语法详解
    iOS
    iOS
    MySQL的事务的处理
    iOS
    iOS AOP编程思想及实践
    iOS 静态库和动态库(库详解)
    iOS 沙盒目录结构及正确使用
  • 原文地址:https://www.cnblogs.com/guapisolo/p/9696978.html
Copyright © 2011-2022 走看看