2024牛客暑期多校训练营3 E. Malfunctioning Typewriter 【概率DP、Trie】

Malfunctioning Typewriter

题意

有一个 01 01 01 打字机,每次输入 0 0 0 1 1 1,会以 p p p 的概率打出正确的数字, 1 − p 1 − p 1p 的概率打出错误的数字。( 0.5 ≤ p < 1 0.5 \leq p < 1 0.5p<1
给定 n n n 个长度均为 m m m 01 01 01 串,要求用打字机以任意顺序将这 n n n个串输出(不能有额外的错误输出),求最大成功概率。

1 ≤ n , m ≤ 1000 1 \leq n,m \leq 1000 1n,m1000

思路

首先对 n n n 个串建立 T r i e Trie Trie
我们会发现:我们输入串的过程等价于在 T r i e Trie Trie 上旅行,那么我们每一次决策会转移到下一个状态,也可能会跳出 T r i e Trie Trie,导致输出完全错误

对于 T r i e Trie Trie 上的一个节点 u u u,记它的左右孩子分别为 l s u , r s u ls_u, rs_u lsu,rsu,记 s z ( u ) sz(u) sz(u) u u u 节点子树下的叶子个数(即有多少 01 01 01 串以该点为前缀)。
那么对于当前点 u u u,我们需要恰好输出 s z ( l s u ) sz(ls_u) sz(lsu) 0 0 0 s z ( r s u ) sz(rs_u) sz(rsu) 1 1 1
记为 f ( s z ( l s u ) ,    s z ( r s u ) ) f(sz(ls_u), \; sz(rs_u)) f(sz(lsu),sz(rsu))

那么我们用 f ( x , y ) f(x, y) f(x,y) 来表示恰好输出 x x x 0 0 0 y y y 1 1 1最大概率
可以发现答案就是: ∏ f ( s z ( l s u ) , s z ( r s u ) ) \prod f(sz(ls_u), sz(rs_u)) f(sz(lsu),sz(rsu))

f f f 可以用概率 D P DP DP 预处理,转移:
f x , y = max ⁡ { p f x − 1 , y + ( 1 − p ) f x , y − 1 ,    p f x , y − 1 + ( 1 − p ) f x − 1 , y } . f_{x, y} = \max \{pf_{x−1, y}+(1−p)f_{x, y−1}, \;pf_{x, y−1}+(1−p)f_{x−1, y} \}. fx,y=max{pfx1,y+(1p)fx,y1,pfx,y1+(1p)fx1,y}.

意思就是在当前状态决策输出 0 0 0 还是 1 1 1,取 max ⁡ \max max

#include<bits/stdc++.h>
#define fore(i,l,r)	for(int i=(int)(l);i<(int)(r);++i)
#define fi first
#define se second
#define endl '\n'
#define ull unsigned long long
#define ALL(v) v.begin(), v.end()
#define Debug(x, ed) std::cerr << #x << " = " << x << ed;

const int INF=0x3f3f3f3f;
const long long INFLL=1e18;

typedef long long ll;

const int N = 1005;
const int M = 1005;

int n, m;
double p;
double f[N][N];
int cnt;
double ans = 1.0;

struct node{
    int sz;
    int son[2];
}tr[N * M];

void insert(std::string& s){
    int now = 0;
    for(auto c : s){
        int ch = c - '0';
        if(!tr[now].son[ch]) tr[now].son[ch] = ++cnt;
        now = tr[now].son[ch];
        ++tr[now].sz;
    }
}

void dfs(int u){
    int l = (tr[u].son[0] ? tr[tr[u].son[0]].sz : 0);
    int r = (tr[u].son[1] ? tr[tr[u].son[1]].sz : 0);
    ans *= f[l][r];
    if(tr[u].son[0]) dfs(tr[u].son[0]);
    if(tr[u].son[1]) dfs(tr[u].son[1]);
}

int main(){
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);
    std::cout.tie(nullptr);

    std::cin >> n >> m >> p;
    fore(i, 0, n){
        std::string s;
        std::cin >> s;
        insert(s);
    }

    f[0][0] = 1;
    fore(i, 1, N) f[i][0] = f[0][i] = p * f[i - 1][0];
    fore(i, 1, N)
        fore(j, 1, N)
            f[i][j] = std::max(p * f[i - 1][j] + (1 - p) * f[i][j - 1], p * f[i][j - 1] + (1 - p) * f[i - 1][j]);

    dfs(0);

    std::cout << std::fixed << std::setprecision(12) << ans << endl;
    
    return 0;
}
  • 19
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值