概率動規小專題

石頭剪刀布

題意:
桌面上有 rr 個石頭, ss 個剪刀和 pp 個布。
每次等概率選擇兩個(種類)不同的物品進行剪刀石頭布,將輸掉的物品移出桌面。
求最後桌面上只剩下石頭的概率,和只剩下剪刀的概率,和只剩下布的概率。保留八位小數。

解法:
dp[i][j][k]\text{dp}[i][j][k] 表示剩餘 ii 個石頭, jj 個剪刀和 kk 個布的概率。則狀態轉移方程爲 dp[i1][j][k]+=dp[i][j][k]P{rock and paper}\text{dp}[i-1][j][k]+=dp[i][j][k]*P\{\text{rock and paper}\} 其中 P{rock and paper}=kiij+jk+kiP\{\text{rock and paper}\}=\frac{ki}{ij+jk+ki} 其他情況類似。

參考代碼:

#include <cstdio>
#include <cstring>
#include <algorithm>
const int MAXN=1e2+10;
double dp[MAXN][MAXN][MAXN];
int r,s,p;
inline double prob(int a,int b,int c){
	return 1.0*a*b/(a*b+b*c+c*a);
}
void calc(int i,int j,int k){
    if (i&&k) dp[i-1][j][k]+=dp[i][j][k]*prob(i,k,j);
    if (i&&j) dp[i][j-1][k]+=dp[i][j][k]*prob(i,j,k);
    if (j&&k) dp[i][j][k-1]+=dp[i][j][k]*prob(j,k,i);
}
void solve(){
    for (int x=0;x<r+s+p;x++)
		for (int i=0;i<=std::min(x,r);i++)
			for (int j=0;j<=std::min(x-i,s);j++)
				calc(r-i,s-j,p-(x-i-j));
    double rr=0,ss=0,pp=0;
    for (int i=1;i<=r;++i)
        rr+=dp[i][0][0];
	for (int i=1;i<=s;++i)
        ss+=dp[0][i][0];
    for (int i=1;i<=p;++i)
        pp+=dp[0][0][i];
	printf("%.8lf %.8lf %.8lf\n",rr,ss,pp);
}
int main(){
//    freopen("k.in","r",stdin);
//    freopen("k.out","w",stdout);
	scanf("%d%d%d",&r,&s,&p);
	memset(dp,0,sizeof(dp));
	dp[r][s][p]=1;
	solve();
}

v and x

題意:
有兩個變量 v,xv,x ,初始全爲 00 。每一次操作有 p1p_1 的概率將 vv 自增 11 ,有 p2p_2 的概率將 xx 自增 vv 。當 xkx\geqslant k 時結束操作。問 xx 的期望。保留六位小數。

解法:
v+xkv+x\geqslant k 時,往後只有兩種情況:一, vv 自增,然後繼續;二, xx 自增,結束。則該情況下的期望爲 E(x)=i=0p1ip2(v+x+i)=p2i=0(p1i(v+x)+ip1i)=p2(v+x1p1+p1(1p1)2)\begin{aligned}E(x)=&amp;\sum_{i=0}^{\infty}p_1^ip_2(v+x+i)\\=&amp;p_2\sum_{i=0}^{\infty}\left(p_1^i(v+x)+ip_1^i\right)\\=&amp;p_2\left(\frac{v+x}{1-p_1}+\frac{p_1}{(1-p_1)^2}\right)\end{aligned}

v+x&lt;kv+x&lt;k 時,期望分爲兩部分,即 E(x)=p1E(x)v=v+1+p2E(x)x=x+vE(x)=p_1E(x)\vert_{v=v+1}+p_2E(x)\vert_{x=x+v}

此外還有邊界條件的處理。因爲 E(x)v=0,x=0E(x)\vert_{v=0,x=0} 的狀態轉移爲 E(x)v=0,x=0=p1E(x)v=1,x=0+p2E(x)v=0,x=0E(x)\vert_{v=0,x=0}=p_1E(x)\vert_{v=1,x=0}+p_2E(x)\vert_{v=0,x=0} 如果直接迭代會沒有結果。但是通過人爲的運算,就用剛纔的那個“無限遞歸式”,我們得到 E(x)v=0,x=0=p11p2E(x)v=1,x=0E(x)\vert_{v=0,x=0}=\frac{p_1}{1-p_2}E(x)\vert_{v=1,x=0} 則下面只需計算 E(x)v=1,x=0E(x)\vert_{v=1,x=0} 即可。

參考代碼:

#include <cstdio>
#include <cstring>
const int MAXN=1e3+10;
double dp[MAXN][MAXN];
int k,pa,pb;
double p1,p2;
double solve(int v,int x){
    if (dp[v][x]) return dp[v][x];
    if (v+x>=k) return dp[v][x]=p2*((v+x)/(1-p1)+p1/(1-p1)/(1-p1));
    return dp[v][x]=p1*solve(v+1,x)+p2*solve(v,x+v);
}
int main(){
    memset(dp,0,sizeof(dp));
    scanf("%d%d%d",&k,&pa,&pb);
    p1=1.0*pa/(pa+pb);
    p2=1.0*pb/(pa+pb);
    printf("%.6lf\n",p1*solve(1,0)/(1-p2));
    return 0;
}

Collecting Bugs

題意:
某軟件有 ss 個子系統,會產生 nn 種bug。某人一天能發現一個bug,不同子系統和種類中發現bug是等可能性的。現發現了 nn 種bug,問每個子系統都發現bug的天數的期望。

解法:
dp[i][j]\text{dp}[i][j] 表示已找到 iijj 個子系統的bug後剩餘天數的期望,則顯然有 dp[n][s]=0\text{dp}[n][s]=0 ,題中所求爲 dp[0][0]\text{dp}[0][0] 。對於一個狀態 dp[i][j]\text{dp}[i][j] ,當新發現了一個bug時,可能會出現:

  1. 發現已有分類和已有子系統的bug,概率爲 p1=injsp_1=\frac{i}{n}\cdot\frac{j}{s}
  2. 發現已有的分類未有子系統的bug,概率爲 p2=insjsp_2=\frac{i}{n}\cdot\frac{s-j}{s}
  3. 發現未有的分類已有子系統的bug,概率爲 p3=ninjsp_3=\frac{n-i}{n}\cdot\frac{j}{s}
  4. 發現未有分類和未有子系統的bug,概率爲 p4=ninsjsp_4=\frac{n-i}{n}\cdot\frac{s-j}{s}

則有 dp[i][j]=1+p1dp[i][j]+p2dp[i][j+1]+p3dp[i+1][j]+p4dp[i+1][j+1]\text{dp}[i][j]=1+p_1\text{dp}[i][j]+p_2\text{dp}[i][j+1]+p_3\text{dp}[i+1][j]+p_4\text{dp}[i+1][j+1]

整理得狀態轉移方程爲 dp[i][j]=(i*(s-j)*dp[i][j+1]+(n-i)*j*dp[i+1][j]+(n-i)*(s-j)*dp[i+1][j+1]+n*s)/(n*s-i*j)

由於是POJ的題目,最後需要用%f輸出而不是%lf

參考代碼:

#include <cstdio>
const int MAXN=1e3+10;
double dp[MAXN][MAXN];
int n,s;
double solve(){
    dp[n][s]=0;
    for (int i=n;i>=0;i--){
        for (int j=s;j>=0;j--){
            if (i==n&&j==s) continue;
            dp[i][j]=(i*(s-j)*dp[i][j+1]+(n-i)*j*dp[i+1][j]+(n-i)*(s-j)*dp[i+1][j+1]+n*s)/(n*s-i*j);
        }
    }
    return dp[0][0];
}
int main(){
    while(~scanf("%d%d",&n,&s)){
        printf("%.4lf\n",solve());
    }
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章