C. Need for Pink Slips


題目鏈接:https://codeforces.com/contest/1543/problem/C

題意:

給定三張牌,每次抽取一張牌,給定這三張牌被抽到的概率,分別記為 \(c,m,p\),並給定一個波動因子 \(v\),設當前抽到的這張牌被抽到的概率為 \(a\),若\(a \le v\),則將當前牌刪除,將其概率 \(a\) 平分給還剩下的牌,若\(a > v\),則將當前牌的概率減少 \(v\),即當前牌的概率變為\(a-v\),並將減少的概率 \(v\) 分給其他牌。問抽到第三張牌的期望抽取次數(包含小數)是多少。
\(t\)組測試樣例(\(1 \le t \le 10\)),每次輸入 \(c,m,p\)\(v\) (\(c,m,p,v\)最多有四位小數)(\(0<c,m,p<1,c+m+p=1,0.1 \le v \le 0.9\)),輸出期望抽取次數(要保證輸出結果與答案的絕對誤差或相對誤差不超過 \({10^{-6}}\),即設輸出結果為 a,答案為 b,則要保證\({ { {\frac {\vert {a-b} \vert} {max {(1,\vert {b} \vert)} } } } \le {10^{-6}} }\)))

思路:

概率 dp

\(dp[c][m][p]\) 為抽到第一張牌的概率為 \(c\),抽到第二張牌的概率為 \(m\),抽到第三張牌的概率為 \(p\) 時抽到第三張牌所需要抽取的期望次數。

我們已知 \(dp[0][0][1] = 1\),而對於其他任意狀態 \(dp[c][m][p]\),從該狀態可以抽取一、二、三中任意一張牌從而轉移到下一個狀態,若抽取的牌為第一或第二張,則根據其當前的概率是否大於 \(v\) 確定下一個狀態,若抽取的牌為第三張,則直接結束,不需要再抽取。

即在當前狀態 \(dp[c][m][p]\) 下可轉移的狀態可描述為:

\( 當前狀態dp[c][m][p] = {\begin{cases} {c概率:抽取一張牌,這張牌為第一張牌,狀態轉移為抽取第一張牌之后的狀態} \\ {m概率:抽取一張牌,這張牌為第二張牌,狀態轉移為抽取第二張牌之后的狀態} \\ {p概率:抽取一張牌,這張牌為第三張牌,抽取結束} \end{cases}} \)

則可以這樣計算當前狀態的期望抽取次數:
當前狀態的期望抽取次數\(dp[c][m][p]\) = (1+抽到第一張牌之后的狀態的期望抽取次數) * c + (1+抽到第二張牌之后的狀態的期望抽取次數) * m + (1+抽到第三張牌之后的狀態的期望抽取次數(0)) * p

而由於轉移到的下一個狀態會受 c 和 m 取值的影響,因此我們還需要對 c 和 m 的大小分情況討論(\(c>0,0<c\le{v},c=0\) 以及 \(m>0,0<m\le{v},m=0,3\times{3}=9\) 種情況,特別地,當 \(c=0,m=0\) 時,p=1,即此種情況即為已知的遞歸終點 \(dp[0][0][1]=1\))

因此轉移式可表達為:

\[dp[c][m][p]={\begin{cases} {({dp[c-v][m+\frac{v}{2}][p+\frac{v}{2}]} + 1)} * c + {({dp[c+\frac{v}{2}][m-v][p+\frac{v}{2}]} + 1)} * m + {(0 + 1) * p}, &{c>v且m>v} \\ {(dp[c-v][m+\frac{v}{2}][p+\frac{v}{2}] + 1)} * c + {(dp[c+\frac{m}{2}][0][p+\frac{m}{2}] + 1)} * m + {(0 + 1) * p}, &{c>v且0<m\le{v}} \\ {(dp[c-v][0][p+v] + 1)} * c + {(0 + 1) * p}, &{c>v且m=0} \\ {(dp[0][m+\frac{c}{2}][p+\frac{c}{2}] + 1)} * c + {(dp[c+\frac{v}{2}][m-v][p+\frac{v}{2}] + 1)} * m + {(0 + 1) * p}, &{0<c\le{v}且m>v} \\ {(dp[0][m+\frac{c}{2}][p+\frac{c}{2}] + 1)} * c + {(dp[c+\frac{m}{2}][0][p+\frac{m}{2}] + 1)} * m + {(0 + 1) * p}, &{0<c\le{v}且0<m\le{v}} \\ {(dp[0][0][p+c] + 1)} * c + {(0 + 1) * p}, &{0<c\le{v}且m=0} \\ {(dp[0][m-v][p+v] + 1)} * m + {(0 + 1) * p}, &{c=0且m>v} \\ {(dp[0][0][p+m] + 1)} * m + {(0 + 1) * p}, &{c=0且0<m\le{v}} \\ 0, &c=0且m=0且p=1 \end{cases}} \]

注意: 由於題目對精度有要求,因此我們需要先將 \(c,m,p,v\) 的值全部擴大精度的倒數倍,然后在搜索中每次需要乘以概率的時候將擴大的值還原為實際概率即可(互相比較相互做減法不需要還原為概率,直接使用擴大的值即可)

代碼:

#include <iostream>
#include <iomanip>
using namespace std;
const double scale = 1e6;
double v;
double dfs(double c, double m, double p)
{ //由於概率之前擴大了,現在每次乘以概率的時候需要將擴大的值還原為實際概率,但是互相比較和相互做減法時不需要還原為概率,直接使用擴大的值即可
    if (c > v)
    {
        if (m > v) //c>v,m>v
            return dfs(c - v, m + v / 2, p + v / 2) * (c / scale) + dfs(c + v / 2, m - v, p + v / 2) * (m / scale) + 1;
        else if (m > 0) //c>v,0<m<=v
            return dfs(c - v, m + v / 2, p + v / 2) * (c / scale) + dfs(c + m / 2, 0, p + m / 2) * (m / scale) + 1;
        else //c>v,m=0
            return dfs(c - v, 0, p + v) * (c / scale) + 1;
    }
    else if (c > 0)
    {
        if (m > v) //0<c<=v,m>v
            return dfs(0, m + c / 2, p + c / 2) * (c / scale) + dfs(c + v / 2, m - v, p + v / 2) * (m / scale) + 1;
        else if (m > 0) //0<c<=v,0<m<=v
            return dfs(0, m + c / 2, p + c / 2) * (c / scale) + dfs(c + m / 2, 0, p + m / 2) * (m / scale) + 1;
        else ////0<c<=b,m=0
            return dfs(0, 0, p + c) * (c / scale) + 1;
    }
    else //c=0
    {
        if (m > v) //c=0,m>v
            return dfs(0, m - v, p + v) * (m / scale) + 1;
        else if (m > 0) //c=0,0<m<=v
            return dfs(0, 0, p + m) * (m / scale) + 1;
        else //c=0,m=0
            return 1;
    }
}
int main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);
    int t;
    cin >> t;
    while (t--)
    {
        double c, m, p;
        cin >> c >> m >> p >> v;
        c *= scale, m *= scale, p *= scale, v *= scale; //同時擴大要求的精度的倒數倍,防止出現精度問題
        cout << fixed << setprecision(12) << dfs(c, m, p) << endl;
    }
}

總結:


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM