POJ 2096Collecting Bugs(概率期望dp)

本文介绍了一个通过动态规划解决的问题:对于一个拥有s个子系统的软件,存在n种不同的bug,每天随机发现一种bug并出现在某个子系统中,求找到所有n种bug并且每个子系统至少发现了一次bug所需的平均天数。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

大牛博客:http://www.cnblogs.com/flipped/p/5230359.html

有s个系统,n种bug,小明每天找出一个bug,可能是任意一个系统的,可能是任意一种bug,即是某一系统的bug概率是1/s,是某一种bug概率是1/n。

求他找到s个系统的bug,n种bug,需要的天数的期望。

分析

计算期望E=∑所有可能需要的天数*概率

找到s个系统n种bug,需要最少max(s,n)天,而可能的天数是无穷的,这样计算很复杂,复杂到算不了。

所以考虑dp,期望E=∑(昨天可以转移到现在状态的所有可能的情况的期望+1)*概率=∑(昨天可以转移到现在状态的所有可能的情况的期望)*概率 +1

一开始我想用dp[i][j]表示已经找到i种bug,j个系统里找到bug,的期望天数,但是这样不能推出来,由【i-1,j】【i,j-1】【i,j】【i-1,j-1】四种状态推的话,需要1天的概率我们知道,但是这四种情况的概率加起来不等于1,也就是还有需要2天3天...的情况,概率很复杂计算更复杂(回到上面的复杂去了)

所以要dp[i][j]表示已经找到i种bug,j个系统里找到bug,离目标还需要的期望天数。

好,如果明天找到一个bug。

它就可以转移到这里:

dp[i+1][j]  不属于i种,属于j个系统之一。  概率为p1=(n-i)/n* j/s 。 

dp[i][j+1]  属于i种之一,不属于j个系统。    概率为p2=(s-j)/s* i/n 

dp[i+1][j+1]  不属于i种,不属于j个系统。     概率为p3=(n-i)/n*(s-j)/s 

dp[i][j]      属于i种之一,属于j个系统之一。  概率为p4=i/n* j/s 

也就是比如找到了新种类,已知系统的bug,那明天离到达目标的期望天数就是dp[i+1][j],那就是今天还差dp[i+1][j]+1天。

dp[i][j]就是它找到的没有用的bug,那明天浪费了,那今天还差dp[i][j]+1天。今天和明天的dp[i][j]是一样的。

p1+p2+p3+p4=1,所以有下面这个式子。

dp[i][j]=dp[i+1][j]*p1+dp[i][j+1]*p2+dp[i+1][j+1]*p3+dp[i][j]*p4 +1

移项然后变成这样:dp[i][j]= ( n*s + (n-i)*j*dp[i+1,j] + i*(s-j)*dp[i,j+1] + (n-i)*(s-j)*dp[i+1,j+1] )/( n*s - i*j )  

 我们知道dp[n][s]=0,就是已经到达目标,还需要0天。然后逆推。

/**
    dp求期望的题。
    题意:一个软件有s个子系统,会产生n种bug。
    某人一天发现一个bug,这个bug属于某种bug,发生在某个子系统中。
    求找到所有的n种bug,且每个子系统都找到bug,这样所要的天数的期望。
    需要注意的是:bug的数量是无穷大的,所以发现一个bug,出现在某个子系统的概率是1/s,
    属于某种类型的概率是1/n。
    解法:
    dp[i][j]表示已经找到i种bug,并存在于j个子系统中,要达到目标状态的天数的期望。
    显然,dp[n][s]=0,因为已经达到目标了。而dp[0][0]就是我们要求的答案。
    dp[i][j]状态可以转化成以下四种:
        dp[i][j]    发现一个bug属于已经找到的i种bug和j个子系统中
        dp[i+1][j]  发现一个bug属于新的一种bug,但属于已经找到的j种子系统
        dp[i][j+1]  发现一个bug属于已经找到的i种bug,但属于新的子系统
        dp[i+1][j+1]发现一个bug属于新的一种bug和新的一个子系统
    以上四种的概率分别为:
    p1 =     i*j / (n*s)
    p2 = (n-i)*j / (n*s)
    p3 = i*(s-j) / (n*s)
    p4 = (n-i)*(s-j) / (n*s)
    又有:期望可以分解成多个子期望的加权和,权为子期望发生的概率,即 E(aA+bB+...) = aE(A) + bE(B) +...
    所以:
    dp[i,j] = p1*dp[i,j] + p2*dp[i+1,j] + p3*dp[i,j+1] + p4*dp[i+1,j+1] + 1;
    整理得:
    dp[i,j] = ( 1 + p2*dp[i+1,j] + p3*dp[i,j+1] + p4*dp[i+1,j+1] )/( 1-p1 )
            = ( n*s + (n-i)*j*dp[i+1,j] + i*(s-j)*dp[i,j+1] + (n-i)*(s-j)*dp[i+1,j+1] )/( n*s - i*j )
**/

#include<cstdio>
#define N 1005
double dp[N][N];
int main()
{
    int n, s;
    while(scanf("%d%d", &n, &s) != EOF)
    {
        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] = (dp[i+1][j]*(n-i)*j+dp[i][j+1]*i*(s-j) + dp[i+1][j+1]*(n-i)*(s-j) + n*s) / (n*s-i*j);
            }
        printf("%.4f",dp[0][0]);
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值