概率动规小专题

石头剪刀布

题意:
桌面上有 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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章