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-10
typedef 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-10
typedef 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;
}
时间: 2025-01-05 04:41:13

codeforces 482c 状压+概率DP的相关文章

UVA 11600 Masud Rana 并查集+状压概率dp

题目链接:点击打开链接 题意:给定一个无向图,给定的边是已经存在的边,每天会任选两个点新建一条边(建过的边还会重建) 问:使得图连通的天数的期望. 思路:状压喽,看别人都是这么写的,n=30,m=0 我也不知道怎么办了.. 当前连通块点数为X 加入一个Y个点的连通块需要的天数为 Y/(n-X); Masud Rana, A Daring Spy Of Bangladesh Counter Intelligence. He is in a new mission. There is a total

HDU 4336 Card Collector(状压 + 概率DP 期望)题解

题意:每包干脆面可能开出卡或者什么都没有,一共n种卡,每种卡每包爆率pi,问收齐n种卡的期望 思路:期望求解公式为:$E(x) = \sum_{i=1}^{k}pi * xi + (1 - \sum_{i = 1}^{k}pi) * [1 + E(x)]$,即能转换到x情况的期望+x情况原地踏步的期望. 因为n比较小,我们可以直接状压来表示dp[x]为x状态时集齐的期望.那么显然dp[111111111] = 0.然后我们状态反向求解.最终答案为dp[0]. 然后来看期望的求解:$E(x) =

HDU-4336 Card Collector(状压概率DP||容斥原理)

Card Collector http://acm.hdu.edu.cn/showproblem.php?pid=4336 Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Special Judge Problem Description In your childhood, do you crazy for collecting the beautiful cards in

HDU 4336 Card Collector 状压+概率dp

题目链接:点击打开链接 题意: 有n种卡片,每吃一包方便面都有一定概率获得其中一种卡片(也可能不获得卡片) 问集齐n张召唤神龙需要吃的方便面包数的期望. 思路: dp[i] 表示已经拥有卡片的状态为i, 还需要吃多少包才能拥有所有卡片, 显然 dp[(1<<n)-1] = 0; (已经拥有卡片就不用吃了嘛) 而答案就是dp[0]; 用样例二举例,下面dp方程内直接用二进制表示,为了方便观察,我们用最高位表示第一张卡片(P1=0.1),最低位表示第n张卡片(P2=0.4) dp[01]  = (

SPOJ BALNUM Balanced Numbers 状压+数位DP

一开始想了一个用二进制状压的方法,发现空间需要的太大,光光memset都要超时 = = 其实不用每次都memset 也可以用三进制,一开始直接打表出所有的状态转移就好 #include <cstdio> #include <cstring> #include <cmath> #include <algorithm> #include <climits> #include <string> #include <iostream&g

Codeforces 482C Game with Strings(dp+概率)

题目链接:Codeforces 482C Game with Strings 题目大意:给定N个字符串,现在从中选定一个字符串为答案串,你不知道答案串是哪个,但是可以通过询问来确定, 每次询问一个位置上字符为多少.现在你询问的每个位置的概率是相同的,(问过的位置不会再问),求询问次数的期 望. 解题思路:因为字符串长度不会大于20,所以用二进制表示询问了哪些位置,C[s]即为询问s的位置可以确定多少个字 符串.这步不能通过枚举s,然后判断处理,复杂度为o(2^20 * 20 * 50),太高.可

codeforces 580d 状压DP

题意:有n种菜,现在选m种菜来吃,如果在吃y的前一道菜是x的话,那么就可以获得额外满意度.每一种菜都有一个满意度. 思路:设dp[i][S]表示为最后一道菜为i,现在的菜吃的状态为S.S中的二进位如果为1表示已经吃了,如果是0则表示没吃,状压DP,答案就出了. #include <cstdio> #include <cstring> #include <cmath> #include <iostream> #include <algorithm>

Codeforces 8C 状压DP

题意:有个人想收拾行李,而n个物品散落在房间的各个角落里(n < 24).现在给你旅行箱的坐标(人初始在旅行箱处),以及n个物品的坐标,你一次只能拿最多两个物品,并且拿了物品就必须放回旅行箱,不能暂时放在地上.问最小的花费是多少?花费是笛卡尔距离的平方. 思路一看n 只有24,应该很容易想到要用状压DP. 那么dp[i]表示i状态并且回到原点的最小花费.那么就暴力枚举拿1个或两个物品放回原点,然后转移就行了.需注意,这个题目中拿物品的顺序对答案无影响,比如先拿1号,再拿2号和先拿2号,再拿1号的

lightoj-1021 - Painful Bases(状压+数位dp)

1021 - Painful Bases PDF (English) Statistics ForumTime Limit: 2 second(s) Memory Limit: 32 MBAs you know that sometimes base conversion is a painful task. But still there are interesting facts in bases. For convenience let's assume that we are deali