几道期望习题(概率dp)

title: 期望习题(概率dp)
date: 2019-08-13 10:57:10
tags: 概率

几道例题

1.Collecting Bugs POJ-2096(期望DP)

题意:有n种bug,s个系统会出现bug,bug数量可以是无穷多的,有一个人一天能够找一个bug,bug出现在各个系统是等概率的,系统中发现的bug也是等概率的属于每种类别,问找到每种bug至少找到一个,每个系统至少找到一个bug的时间期望.

题解:由题意可知,一个bug出现在某个系统的概率是 1 s \frac{1}{s} s1,属于某个类型的概率是 1 n \frac{1}{n} n1

设dp[i] [j]表示已经在i个系统中找到了 j种bug,距离全部找到(即达到目标状态)还需要天数的期望,所以有dp[s] [n]=0,本题采用逆推求期望,最终要求的即为dp[0] [0].

所以dp[i] [j]可由以下状态推得:

  1. dp[i] [j] —>旧系统,旧bug(即这个bug是在已经找到过bug的系统中再找到的,并且已经找到过了它同种类的bug已经被找到过了)

    P 1 = i s ∗ j n P_1=\frac{i}{s}*\frac{j}{n} P1=sinj

  2. dp[i+1] [j] —>新系统,旧bug

    P 2 = ( s − i ) s ∗ j n P_2=\frac{\left( s-i \right)}{s}*\frac{j}{n} P2=s(si)nj

  3. dp[i] [j+1]—>旧系统,新bug

    P 3 = i s ∗ ( n − j ) n P_3=\frac{i}{s}*\frac{\left( n-j \right)}{n} P3=sin(nj)

  4. dp[i+1] [j+1]—>新系统,新bug

    P 4 = ( s − i ) s ∗ ( n − j ) n P_4=\frac{\left( s-i \right)}{s}*\frac{\left( n-j \right)}{n} P4=s(si)n(nj)

所以有:dp[i] [j]= P 1 P_1 P1 *dp[i] [j] + P 2 P_2 P2 *dp[i+1] [j]+ P 3 P_3 P3 *dp[i] [j+1]+ P 4 P_4 P4 *dp[i+1 [j+1]+1

化简得: d p [ i ] [ j ] = ( s − i ) ∗ j ∗ d p [ i + 1 ] [ j ] + i ∗ ( n − j ) ∗ d p [ i ] [ j + 1 ] + ( s − i ) ( n − j ) ∗ d p [ i + 1 ] [ j + 1 ] + n s s n − i j dp\left[ i \right] \left[ j \right] =\frac{\left( s-i \right) *j*dp\left[ i+1 \right] \left[ j \right] +i*\left( n-j \right) *dp\left[ i \right] \left[ j+1 \right] +\left( s-i \right) \left( n-j \right) *dp\left[ i+1 \right] \left[ j+1 \right] +ns}{sn-ij} dp[i][j]=snij(si)jdp[i+1][j]+i(nj)dp[i][j+1]+(si)(nj)dp[i+1][j+1]+ns

#include <iostream>
#include <map>
#include <algorithm>
#include <cstring>
#include <queue>
const int maxn=1005;
using namespace std;
double dp[maxn][maxn];
int main()
{
	int n,s;
	scanf("%d%d",&n,&s);
	dp[s][n]=0.0;
	for(int i=s;i>=0;i--)
	{
		for(int j=n;j>=0;j--)
		{
			if(i==s&&j==n)
			continue;
			dp[i][j]=((s-i)*j*dp[i+1][j]+i*(n-j)*dp[i][j+1]+(s-i)*(n-j)*dp[i+1][j+1]+n*s)*1.0/(s*n-i*j);
			//cout<<dp[i][j]<<"**";
		}
	}
	printf("%.4lf\n",dp[0][0]);
	return 0; 
}

2.Race to 1 Again LightOJ - 1038 (期望DP)

题意:给出一个数D,在1~D中选一个它的因子,让D除以这个因子得到一个新的D,这个新的D再选一个因子继续除,直到D=1,求进行的除法次数的期望

题解:设dp[i]表示由i变成1的除法次数期望,易知dp[1]=0

设j是i的一个因子,dp[i]由dp[j]转移而来

d p [ i ] = Σ d p [ j ] n u m + 1 dp\left[ i \right] =\frac{\varSigma dp\left[ j \right]}{num}+1 dp[i]=numΣdp[j]+1(设i=k*j,k为选择的因子,1≤k≤i,num表示i的因子个数)

当k=1时, d p [ i ] = d p [ i ] + Σ d p [ j ] n u m + 1 dp\left[ i \right] =\frac{dp\left[ i \right] +\varSigma dp\left[ j \right]}{num}+1 dp[i]=numdp[i]+Σdp[j]+1(j<i)dp[i]

化简可以得到 d p [ i ] = n u m + Σ d p [ j ] n u m − 1 dp\left[ i \right] =\frac{num+\varSigma dp\left[ j \right]}{num-1} dp[i]=num1num+Σdp[j](j<i)

dp[2]=(2+dp[1])/1=2

#include <iostream>
#include <map>
#include <algorithm>
#include <cstring>
#include <queue>
const int maxn=10e5+5;
using namespace std;
double dp[maxn];	
int main()
{
	int num,j,n,t;
	dp[1]=0.0,dp[2]=2.0;

	for(int i=3;i<=100000;i++)
	{
		num=0;
		for(j=2;j*j<i;j++) 
		{
			if(i%j==0)
			{
				dp[i]+=dp[j]+dp[i/j];
				num+=2;
			}
		}	
		if(j*j==i)
			{
				num++;
				dp[i]+=dp[j];
			} 
		num+=2;//记得另外算上除以因子1和i本身的情况 
		dp[i]=(num+dp[i])/(num-1);
	}
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
	{
		scanf("%d",&t);
		printf("Case %d: %lf\n",i,dp[t]);
	}
	return 0; 
}

3.Dice (III) LightOJ - 1248 (期望+几何分布)

题意:给你一个有n个面的骰子,问你这n面都出现过的投掷次数的期望

题解:设dp[i]为扔出i个不同面后,到达目标n还需要的投掷次数的期望,dp[n]=0;

每次投掷其实只有两种情况:出现过的 P 1 = i n P_1=\frac{i}{n} P1=ni和 没出现过的 P 2 = n − i n P_2=\frac{n-i}{n} P2=nni

所以有: d p [ i ] = i n d p [ i ] + n − i n d p [ i + 1 ] + 1 dp\left[ i \right] =\frac{i}{n}dp\left[ i \right] +\frac{n-i}{n}dp\left[ i+1 \right] +1 dp[i]=nidp[i]+nnidp[i+1]+1

化简得: d p [ i ] = d p [ i + 1 ] + n n − i dp\left[ i \right] =dp\left[ i+1 \right] +\frac{n}{n-i} dp[i]=dp[i+1]+nin

#include <iostream>
#include <map>
#include <algorithm>
#include <cstring>
#include <queue>
const int maxn=10e5+5;
using namespace std;
double dp[maxn];	
int main()
{
	int t;
	cin>>t;
	for(int i=1;i<=t;i++)
	{
		int n;
		cin>>n;
		dp[n]=0;
		for(int j=n-1;j>=0;j--)
		dp[j]=dp[j+1]+n*1.0/(n-j);
		printf("Case %d: %.7lf\n",i,dp[0]);
	}
	
	return 0;
}

4.Activation HDU-4089 (概率DP+方程组)

题意:有n个人在排队注册游戏账号,对于每个用户,都可能遇到以下情况:

  1. 处理失败,该用户继续停留在原位等待重新注册,发生的概率为 P 1 P_1 P1
  2. 处理错误,该用户到队尾重新排队,发生的概率为 P 2 P_2 P2
  3. 处理成功,该用户出队,发生的概率为 P 3 P_3 P3
  4. 服务器故障,所有用户都不能注册了,发生的概率为 P 4 P_4 P4

Tomato现在排在第m个位置,求他排在第k位以前(包括第k位)时服务器故障的概率

题解:设dp[i] [j]表示队伍人数为i时,Tomato排在第j位的概率,我们最终要求的结果就是dp[n] [m]

状态转移方程:

  1. j==1时, d p [ i ] [ 1 ] = P 1 ∗ d p [ i ] [ 1 ] + P 2 ∗ d p [ i ] [ i ] + P 4 dp[i] [1]=P_1*dp[i] [1]+P_2*dp[i] [i]+P_4 dp[i][1]=P1dp[i][1]+P2dp[i][i]+P4

    ( P 3 P_3 P3对应的情况:如果Tomato成功,出队了就成了dp[i] [0],而dp[i] [0]=0,就像如果 P 4 P_4 P4=0,即不存在服务器故障情况,输出的概率就为0了)

  2. 1<j≤k时, d p [ i ] [ j ] = P 1 ∗ d p [ i ] [ j ] + P 2 ∗ d p [ i ] [ j − 1 ] + P 3 ∗ d p [ i − 1 ] [ j − 1 ] + P 4 dp[i] [j]=P_1*dp[i] [j]+P_2*dp[i] [j-1]+P_3*dp[i-1] [j-1]+P_4 dp[i][j]=P1dp[i][j]+P2dp[i][j1]+P3dp[i1][j1]+P4

  3. j>k时, d p [ i ] [ j ] = P 1 ∗ d p [ i ] [ j ] + P 2 ∗ d p [ i ] [ j − 1 ] + P 3 ∗ d p [ i − 1 ] [ j − 1 ] dp[i] [j]=P_1*dp[i] [j]+P_2*dp[i] [j-1]+P_3*dp[i-1] [j-1] dp[i][j]=P1dp[i][j]+P2dp[i][j1]+P3dp[i1][j1] (这里不能服务器故障,不然题目要求的事件就不会发生了)

要化简上面式子:
在这里插入图片描述

x 2 = P 2 1 − P 1 x_2=\frac{P_2}{1-P_1} x2=1P1P2 x 3 = P 3 1 − P 1 x_3=\frac{P_3}{1-P_1} x3=1P1P3 x 4 = P 4 1 − P 1 x_4=\frac{P_4}{1-P_1} x4=1P1P4

如果我们对i从1->n递推的话,dp[i-1] [j-1]相对dp[i] [j-1]就可以看作是已经求解的常数项进行处理,

d p [ i ] [ 1 ] = x 2 ∗ d p [ i ] [ i ] + c [ 1 ] dp[i] [1]=x_2*dp[i] [i]+c[1] dp[i][1]=x2dp[i][i]+c[1] c [ 1 ] = x 4 c[1]=x_4 c[1]=x4  (j==1时)

d p [ i ] [ j ] = x 2 ∗ d p [ i ] [ j − 1 ] + c [ j ] dp[i] [j]=x_2*dp[i] [j-1]+c[j] dp[i][j]=x2dp[i][j1]+c[j] c [ j ] = x 3 ∗ d p [ i − 1 ] [ j − 1 ] + x 4 c[j]=x_3* dp[i-1] [j-1]+x_4 c[j]=x3dp[i1][j1]+x4  (j>k时不加 x 4 x_4 x4)

迭代求解 d p [ i ] [ i ] dp[i][i] dp[i][i]:

求出 d p [ i ] [ i ] dp[i] [i] dp[i][i]后可根据 d p [ i ] [ 1 ] = x 2 ∗ d p [ i ] [ i ] + c [ 1 ] dp[i] [1]=x_2*dp[i][i]+c[1] dp[i][1]=x2dp[i][i]+c[1],推出 d p [ i ] [ 1 ] dp[i] [1] dp[i][1],从而推出所有的 d p [ i ] [ j ] dp[i][j] dp[i][j]

注意:要特判p4=0的情况,题目的内存限制很小,32768 kB= 2 25 2^{25} 225Byte,大概double型占8Byte,大概只能开 2 22 2^{22} 222即大概 4 ∗ 1 0 6 4*10^6 4106 大小的double型数组,而根据题目 n ∗ m n*m nm, d p [ n ] [ m ] dp[n][m] dp[n][m]就已经 4 ∗ 1 0 6 4*10^6 4106,何况还有别的数组要占用内存,所以肯定是不够的,又因为只用到 d p [ i − 1 ] [   ] dp[i-1][\ ] dp[i1][ ] d p [ i ] [   ] dp[i][\ ] dp[i][ ]的关系,所以用滚动数组进行处理

#include <iostream>
#include <map>
#include <algorithm>
#include <cstring>
#include <queue>
#include <cmath>
#include <stdlib.h>
const int maxn=2005;
const double eps=1e-6;
using namespace std;
double dp[maxn][maxn];	
int main()
{
	int n,m,k;
	double p1,p2,p3,p4;
	double x2_pow[maxn],c[maxn];
	while(scanf("%d%d%d%lf%lf%lf%lf",&n,&m,&k,&p1,&p2,&p3,&p4)==7)
	{
		if(fabs(p4)<eps)
		{
			printf("0.00000\n");
			continue;	
		}
		double x2=p2/(1-p1);
		double x3=p3/(1-p1);
		double x4=p4/(1-p1);
		x2_pow[0]=1.0;
		for(int i=1;i<=n;i++)
			x2_pow[i]=x2_pow[i-1]*x2;//求x2的次幂 
	
		c[1]=x4;
		dp[1][1]=x4/(1-x2);
		for(int i=2;i<=n;i++)
		{
			for(int j=2;j<=k;j++)
			c[j]=x3*dp[(i-1)&1][j-1]+x4;
			for(int j=k+1;j<=i;j++)
			c[j]=x3*dp[(i-1)&1][j-1];//求好c[j] 
			
			double sum=0.0;
			for(int j=1;j<=i;j++)
				sum+=x2_pow[i-j]*c[j];
			
			dp[i&1][i]=sum/(1-x2_pow[i]);
			dp[i&1][1]=x2*dp[i&1][i]+c[1];
			for(int j=2;j<i;j++)
			dp[i&1][j]=x2*dp[i&1][j-1]+c[j];
		}
		
		printf("%.5lf\n",dp[n&1][m]);
	}
	return 0;
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值