hdu 4089

hdu 4089

题意:有n个人排队等着在官网上激活游戏。Tomato排在第m个。
对于队列中的第一个人。有一下情况:
1、激活失败,留在队列中等待下一次激活(概率为p1)
2、失去连接,出队列,然后排在队列的最后(概率为p2)
3、激活成功,离开队列(概率为p3)
4、服务器瘫痪,服务器停止激活,所有人都无法激活了。
求服务器瘫痪时Tomato在队列中的位置<=k的概率

解析:
概率DP;

设dp[i][j]表示i个人排队,Tomato排在第j个位置,达到目标状态的概率(j<=i)
dp[n][m]就是所求

j==1:    dp[i][1]=p1*dp[i][1]+p2*dp[i][i]+p4;(此时tomato只有三种选择,不能选注册成功)

2<=j<=k: dp[i][j]=p1*dp[i][j]+p2*dp[i][j-1]+p3*dp[i-1][j-1]+p4;(此时,队列里的第一人有4中可能选择,若为p1对应事件,那么第一人位置不动,则tomato位置不动,这种情况概率为p1*dp[i][j] ,若为p2对应事件,那么第一人排到最后,tomato前进一位,对应概率p2*dp[i][i]······。)

k<j<=i:  dp[i][j]=p1*dp[i][j]+p2*dp[i][j-1]+p3*dp[i-1][j-1];(分析同上)

化简:
j==1:    dp[i][1]=p*dp[i][i]+p41;                        ①
2<=j<=k: dp[i][j]=p*dp[i][j-1]+p31*dp[i-1][j-1]+p41;     ②
k<j<=i:  dp[i][j]=p*dp[i][j-1]+p31*dp[i-1][j-1];         ③

其中:
p21=p2/(1-p1);
p31=p3/(1-p1)
p41=p4/(1-p1)

我们可以求得dp[1][1] = p41/(1-p21);
①中dp[i][i]为未知数(dp[i-1][j-1]以求)
而在②或③中,dp[i][j]都是由前一步的dp[i][j-1]得来,即比如:
dp[3][1]=dp[3][3]+c1;
dp[3][2]=dp[3][1]*p31+c2;
dp[3][3]=dp[3][2]*p31+c3;
因为dp[3][3]最开始不知道,所以dp[3][1]不知道,所以dp[3][2]不知道,但是最后有dp[3][3];
所以联立这3个方程可解出dp[3][3],然后求dp[3][1],dp[3][2]...

见代码注释部分:

注意特判一种情况。就是p4<eps时候,就不会崩溃了,应该直接输出0
*/

 
double dp[Maxn][Maxn]; //dp[i][j]表示一共有i个人,该人排在第j位时的发生所求事件的概率
double p1,p2,p3,p4,temp[Maxn]; //temp[i]表示求dp[i][j] 时前面一层的常数
double pp[Maxn]; //pp[i]表示pp^i,用于迭代求出dp[i][i];
int n,m,k;
 
int main()
{
    double pp2,pp3,pp4;
 
    while (~ scanf ( "%d%d%d" ,&n,&m,&k))
    {
       scanf ( "%lf%lf%lf%lf" ,&p1,&p2,&p3,&p4);
       if (p4<=eps) //最后一步肯定要*P4 ,如果p4很小的话,直接输出0.00000
       {
          puts ( "0.00000" );
          continue ;
       }
 
       pp2=p2/(1-p1);pp3=p3/(1-p1);
       pp4=p4/(1-p1);
 
       pp[0]=1.0;
       for ( int i=1;i<=n;i++)
          pp[i]=pp[i-1]*pp2; //用于迭代保存系数
       dp[1][1]=p4/(1-p1-p2); //根据方程1很快可以求出
       for ( int i=2;i<=n;i++)
       {
          temp[1]=pp4;
          for ( int j=2;j<=k;j++)
             temp[j]=pp3*dp[i-1][j-1]+pp4;
          for ( int j=k+1;j<=i;j++)
             temp[j]=pp3*dp[i-1][j-1]; //第j个方程的常数
          double tmp=0.0; //求出只含dp[i][i]未知数方程的常数部分
 
          for ( int j=i;j>=1;j--)
             tmp+=temp[j]*pp[i-j]; //把前面的方程带入后面的方程中
          dp[i][i]=tmp/(1-pp[i]);
          dp[i][1]=pp2*dp[i][i]+temp[1]; //带入方程求出后面未知数的值
          for ( int j=2;j<i;j++)
             dp[i][j]=pp2*dp[i][j-1]+temp[j];
       }
       printf ( "%0.5f\n" ,dp[n][m]);
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值