1. 程式人生 > >CodeForces 482C Game with Strings (概率+狀壓DP 好題)* 第四百篇博文紀念

CodeForces 482C Game with Strings (概率+狀壓DP 好題)* 第四百篇博文紀念

#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;
}