1. 程式人生 > >CF482C Game with Strings (狀壓DP+期望DP)

CF482C Game with Strings (狀壓DP+期望DP)

csu span fine 遊戲 pen pre 重復 http string

題目大意:甲和乙玩遊戲,甲給出n(n<=50)個等長的字符串(len<=20),然後甲選出其中一個字符串,乙隨機詢問該字符串某一位的字符(不會重復詢問一個位置),求乙能確定該串是哪個字符串的詢問次數的期望值

這題不看題解好難想......(感謝zhx和zhx兩位大佬的題解)

len很小,考慮狀壓DP,顯然我們要狀壓詢問,要定義兩個狀態,f[]和num[]

1表示詢問,0表示未詢問

那麽,我們定義f[s]表示詢問狀態s距離確定一個字符串所需要的期望值。

定義tot是s狀態剩余的詢問的次數,那麽顯然技術分享圖片因為詢問剩余的每一位的概率是相等的

而一個詢問並不一定能確定唯一一個字符串,可能是好幾個,所以我們要處理這個詢問狀態能確定幾個字符串,即num[s]

而num[s]並不能通過暴力枚舉得到,所以我們只能通過枚舉它的父集來獲取它的狀態

我們把字符串兩兩匹配,定義技術分享圖片為s狀態不能確定的串(用二進制表示哪些串不能確定),接著我們從大到小枚舉狀態,每個子集都從它的父集更新,取所有技術分享圖片的並集,其中1的數量即為num

tip1:而如果1的數量為1,該狀態仍然可以確定所有的串,所以此時num是0

最後我們得到一個很玄學的方程

技術分享圖片

最後的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 }

CF482C Game with Strings (狀壓DP+期望DP)