CodeForces 482C Game with Strings (概率+状压DP 好题)* 第四百篇博文纪念

题目链接:http://codeforces.com/problemset/problem/482/C

#include<bits/stdc++.h>
using namespace std;

#define debug puts("YES");
#define rep(x,y,z) for(int (x)=(y);(x)<(z);(x)++)
#define ll unsigned long long

#define lrt int l,int r,int rt
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
#define root l,r,rt
#define mst(a,b) memset((a),(b),sizeof(a))
const int  maxn =(1<<20)+5;///加括号
const int mod=9999991;
const int ub=1e6;
ll powmod(ll x,ll y){ll t; for(t=1;y;y>>=1,x=x*x%mod) if(y&1) t=t*x%mod; return t;}
ll gcd(ll x,ll y){return y?gcd(y,x%y):x;}

/*
题目大意:给定若干个串,
然后开始流程是这样,一个人随机从中选一个串,
问另一个人通过问位置上的字符的期望次数可以确定这个串(保证所有串都不一样)

由于串的长度只有20,不难想到要用状压dp。
首先我们需要确定一个状态s能标识出多少个不同串,
朴素算法是对于每一个状态,暴力枚举,但是复杂度不支持。
发现其中很多子状态是包含关系,比如一个s不能确定的状态是t,
那么t肯定也存在于s 的子集中,
这样先用状压搞定标识数组,先通过枚举任意两个串,
提取串中相同字符 的状态,初始化标识数组,然后通过从大到小枚举状态来标记。

得到标识数组后,设定dp数组,即在走到s状态后仍然无法确定所得串的概率。
可以用这个dp数组去状压扩充,我们只要搞清扩充的逻辑是什么就可以了。
假如我们得到了u状态的概率,那么对于下一个位置的选择,其概率是1/(n-bit),
bit是u状态的1的个数,然后对于选中目标串的概率,我们用标识数组去处理,
这样就可以扩充dp数组。

时间复杂度:O(2^20*20),内存不注意的话可能会爆掉,把bitcount数组内联。
*/
char s[55][22];
int n,t,l;

int mark[maxn];
ll tmp[maxn];///状态s能区别出多少个
double dp[maxn];///dp状态s表示选到s状态后没有得到目标串的概率

inline ll bitcount(ll x){return x?bitcount(x>>1)+(x&1):0;}

void getmark()
{
    rep(i,0,n) rep(j,i+1,n)
    {
        int S=0;
        rep(k,0,l)
        {
            S+=((s[i][k]==s[j][k])<<k);
        }
        tmp[S]|=((1LL<<i)|(1LL<<j));
    }
    for(int i=t-1;i>=0;i--)
    {
        rep(j,0,l) if(i&(1<<j))
        {
            tmp[i^(1<<j)]|=tmp[i];
        }
        mark[i]=n-bitcount(tmp[i]);
    }
}

double p[55];

int main()
{
    scanf("%d",&n);
    for(int i=0;i<n;i++) scanf("%s",s[i]);
    l=strlen(s[0]),t=(1<<l);///扩充成总状态
    getmark();
    ///rep(i,0,t) cout<<mark[i]<<" ";puts("");
    dp[0]=1.0;
    rep(i,0,t)
    {
        int bit=bitcount(i),tot=n-mark[i];
        double mv=dp[i]/(l-bit);
        if(tot==0) continue;
        rep(j,0,l) if((i&(1<<j))==0)
        {
            int ts=i^(1<<j);
            double a=1.0*(mark[ts]-mark[i])/tot;
            dp[ts]+=mv*(1-a);
            p[bit+1]+=mv*a;
        }
    }
    double ans=0;rep(i,1,l+1) ans+=p[i]*i;
    printf("%.9f\n",ans);///这里还不能用.lf
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_37451344/article/details/84168915