首页 > 代码库 > codeforces 482c 状压+概率DP

codeforces 482c 状压+概率DP

题意:给出N个不同的串,长度一样,别人随机选一个串,你要询问他那个串某一个位置是什么字符直到能确定那个串才能停止,问询问次数的期望。

题解:50个串20个位置容易想到状压,把字符串长度状压先考虑能否在某一个状态确定哪些字符串能确定哪些不能确定,需要2^m*m次,然后时间上不能再乘以n不然会爆,想想只要我知道到达某一个猜位置状态的概率dp[i],再知道相对应有哪些字符串可以确定和不可以确定,用f[i]来表示,那么对于不能确定的字符串相当于就要再猜一步,那么加上这个状态的概率就行了,不会再需要乘以n。

求每个状态对应有哪些可以确定的方法可以把两个字符串两两对比起来,对于所有相等位置求出这些位置也不能确定则标记为1,否则标记为0,这样一圈标记下来之后对于f[i]数组如果在i状态下两个字符串不能互相识别,那么必定被标记为1,当然如果对于f[i]如果某个状态的是包含于另一个状态的则要进行或运算,说着有点麻烦,不过看一下代码就清楚了

#include <cstdio>#include <cstring>#include <cmath>#include <iostream>#include <algorithm>#include <map>#include <vector>#include <queue>using namespace std;#define EPS 1e-10typedef long long ll;const int maxn = (1<<20) + 10;const int INF = 1e9 ;const double eps = 1e-8;const int mod = 2520;ll f[maxn],bin[60];double dp[maxn];int n;char ss[55][25];int cal(ll u){    int cnt = 0;    while(u > 0){        if(u % 2 == 1) cnt++;        u /= 2;    }    return cnt;}int main(){  //  freopen("in.txt","r",stdin);    cin>>n;   // for(int i = 1;i <= 20;i++)    if(n == 1){        cout<<0.000000000<<endl;        return 0;    }    for(int i = 0;i < n;i++)        scanf("%s",ss[i]);    bin[0] = 1;    for(int i = 1;i <= 50;i++) bin[i] = bin[i-1]*2;    int m = strlen(ss[0]);    for(int i = 0;i < n;i++)    for(int j = 0;j < n;j++){        if(i != j){            int same = 0;            for(int k = 0;k < m;k++)                if(ss[i][k] == ss[j][k])                    same |= (bin[k]);            f[same] |= (bin[j]);        }    }    //cout<<(1<<40)<<endl;    int se =(1<<m) - 1;    for(int i = se;i >= 0;i--){        for(int j = 0;j < m;j++)            if(i & bin[j])            f[i^bin[j]] |= f[i];    }    f[0] = bin[n] - 1;   // for(int i = 0;i <= se;i++)      //  cout<<i<<" "<<f[i]<<endl;    double res = 0.0;    dp[0] = 1.0;    for(int i = 0;i <= se;i++){        double cnt = m - cal((ll)i) + 1;        for(int j = 0;j < m;j++){            if((i&bin[j])&&f[i^bin[j]] > 0){               // double cnt1 = cal(f[i^bin[j]]);                dp[i] += dp[i^bin[j]]*(1.0/cnt);            }        }        //cout<<i<<" "<<dp[i]<<endl;    }    for(int i = 0;i <= se;i++){        for(int j = 0;j < n;j++)            if(f[i]&bin[j])                res += dp[i];    }    printf("%.10f\n",res/n);    return 0;}

还有我利用公式求出dp[i],i相对应猜了x次,猜中了的概率为y这三者相乘不行,不知道为什么,先贴一下代码

#include <cstdio>#include <cstring>#include <cmath>#include <iostream>#include <algorithm>#include <map>#include <vector>#include <queue>using namespace std;#define EPS 1e-10typedef long long ll;const int maxn = (1<<20) + 10;const int INF = 1e9 ;const double eps = 1e-8;const int mod = 2520;ll f[maxn];double dp[maxn];int n;char ss[55][25];int cal(ll u){    int cnt = 0;    while(u > 0){        if(u % 2 == 1) cnt++;        u /= 2;    }    return cnt;}int main(){  //  freopen("in.txt","r",stdin);    cin>>n;    for(int i = 0;i < n;i++)        scanf("%s",ss[i]);    int m = strlen(ss[0]);    for(int i = 0;i < n;i++)    for(int j = 0;j < n;j++){        if(i != j){            int same = 0;            for(int k = 0;k < m;k++)                if(ss[i][k] == ss[j][k])                    same |= (1<<k);            f[same] |= (1<<j);        }    }    int se =(1<<m) - 1;    for(int i = se;i >= 0;i--){        for(int j = 0;j < m;j++)            if(i & (1<<j))            f[i^(1<<j)] |= f[i];    }    f[0] = (1<<n) - 1;   // for(int i = 0;i <= se;i++)      //  cout<<i<<" "<<f[i]<<endl;    double res = 0.0;    dp[0] = 1.0;    for(int i = 0;i <= se;i++){        double cnt = m - cal((ll)i) + 1;        for(int j = 0;j < m;j++){            if(i & (1<<j)&&f[i^(1<<j)] > 0){                double cnt1 = cal(f[i^(1<<j)]);                dp[i] += dp[i^(1<<j)]*(1.0/cnt)*cnt1/n;            }        }        //cout<<i<<" "<<dp[i]<<endl;    }    for(int i = 0;i <= se;i++){        res += dp[i]*(cal((ll)i))*(n - cal(f[i]))/n;        //cout<<i<<" "<<dp[i]*(cal((ll)i))*(n - cal(f[i]))/n<<endl;    }    printf("%.10f\n",res);    return 0;}

 

codeforces 482c 状压+概率DP