题意:
桌面上有 r r r 个石头, s s s 个剪刀和 p p p 个布。
每次等概率选择两个(种类)不同的物品进行剪刀石头布,将输掉的物品移出桌面。
求最后桌面上只剩下石头的概率,和只剩下剪刀的概率,和只剩下布的概率。保留八位小数。
解法:
用 dp [ i ] [ j ] [ k ] \text{dp}[i][j][k] dp[i][j][k] 表示剩余 i i i 个石头, j j j 个剪刀和 k k k 个布的概率。则状态转移方程为 dp [ i − 1 ] [ j ] [ k ] + = d p [ i ] [ j ] [ k ] ∗ P { rock and paper } \text{dp}[i-1][j][k]+=dp[i][j][k]*P\{\text{rock and paper}\} dp[i−1][j][k]+=dp[i][j][k]∗P{rock and paper} 其中 P { rock and paper } = k i i j + j k + k i P\{\text{rock and paper}\}=\frac{ki}{ij+jk+ki} P{rock and paper}=ij+jk+kiki 其他情况类似。
参考代码:
#include
#include
#include
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 , x v,x v,x ,初始全为 0 0 0 。每一次操作有 p 1 p_1 p1 的概率将 v v v 自增 1 1 1 ,有 p 2 p_2 p2 的概率将 x x x 自增 v v v 。当 x ⩾ k x\geqslant k x⩾k 时结束操作。问 x x x 的期望。保留六位小数。
解法:
当 v + x ⩾ k v+x\geqslant k v+x⩾k 时,往后只有两种情况:一, v v v 自增,然后继续;二, x x x 自增,结束。则该情况下的期望为 E ( x ) = ∑ i = 0 ∞ p 1 i p 2 ( v + x + i ) = p 2 ∑ i = 0 ∞ ( p 1 i ( v + x ) + i p 1 i ) = p 2 ( v + x 1 − p 1 + p 1 ( 1 − p 1 ) 2 ) \begin{aligned}E(x)=&\sum_{i=0}^{\infty}p_1^ip_2(v+x+i)\\=&p_2\sum_{i=0}^{\infty}\left(p_1^i(v+x)+ip_1^i\right)\\=&p_2\left(\frac{v+x}{1-p_1}+\frac{p_1}{(1-p_1)^2}\right)\end{aligned} E(x)===i=0∑∞p1ip2(v+x+i)p2i=0∑∞(p1i(v+x)+ip1i)p2(1−p1v+x+(1−p1)2p1)
当 v + x < k v+x<k v+x<k 时,期望分为两部分,即 E ( x ) = p 1 E ( x ) ∣ v = v + 1 + p 2 E ( x ) ∣ x = x + v E(x)=p_1E(x)\vert_{v=v+1}+p_2E(x)\vert_{x=x+v} E(x)=p1E(x)∣v=v+1+p2E(x)∣x=x+v
此外还有边界条件的处理。因为 E ( x ) ∣ v = 0 , x = 0 E(x)\vert_{v=0,x=0} E(x)∣v=0,x=0 的状态转移为 E ( x ) ∣ v = 0 , x = 0 = p 1 E ( x ) ∣ v = 1 , x = 0 + p 2 E ( x ) ∣ v = 0 , x = 0 E(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=p1E(x)∣v=1,x=0+p2E(x)∣v=0,x=0 如果直接迭代会没有结果。但是通过人为的运算,就用刚才的那个“无限递归式”,我们得到 E ( x ) ∣ v = 0 , x = 0 = p 1 1 − p 2 E ( x ) ∣ v = 1 , x = 0 E(x)\vert_{v=0,x=0}=\frac{p_1}{1-p_2}E(x)\vert_{v=1,x=0} E(x)∣v=0,x=0=1−p2p1E(x)∣v=1,x=0 则下面只需计算 E ( x ) ∣ v = 1 , x = 0 E(x)\vert_{v=1,x=0} E(x)∣v=1,x=0 即可。
参考代码:
#include
#include
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;
}
题意:
某软件有 s s s 个子系统,会产生 n n n 种bug。某人一天能发现一个bug,不同子系统和种类中发现bug是等可能性的。现发现了 n n n 种bug,问每个子系统都发现bug的天数的期望。
解法:
用 dp [ i ] [ j ] \text{dp}[i][j] dp[i][j] 表示已找到 i i i 种 j j j 个子系统的bug后剩余天数的期望,则显然有 dp [ n ] [ s ] = 0 \text{dp}[n][s]=0 dp[n][s]=0 ,题中所求为 dp [ 0 ] [ 0 ] \text{dp}[0][0] dp[0][0] 。对于一个状态 dp [ i ] [ j ] \text{dp}[i][j] dp[i][j] ,当新发现了一个bug时,可能会出现:
则有 dp [ i ] [ j ] = 1 + p 1 dp [ i ] [ j ] + p 2 dp [ i ] [ j + 1 ] + p 3 dp [ i + 1 ] [ j ] + p 4 dp [ 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]=1+p1dp[i][j]+p2dp[i][j+1]+p3dp[i+1][j]+p4dp[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
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;
}