概率dp——hdu4089推公式+循环迭代

迭代是化简公式的常用技巧

dp[i][j]表示队伍中有i人,tomato排在第j位出现情况2的概率,那么先推出公式再进行简化

dp[i][1]=p21*dp[i][i] + p41
j<=k : dp[i][j]=p21*dp[i][j-1] + p31*dp[i-1][j-1] +p41
j>k : dp[i][j]=p21*dp[i][j-1] + p31*dp[i-1][j-1]

可见公式中的dp[i][1]和dp[i][i]是相关的,那么dp[i][j]也和dp[i][i]相关,所以只要迭代求出dp[i][i],然后再递推即可

在求dp[i][j]时所有dp[i-1][j]已经求出,所以后面的都是常数
因为dp[i][1]和dp[i][i]有关,所以需要迭代算方程先求出dp[i][i]
c数组是后面的常数,先处理出来即可

注意用滚动数组压缩内存,并特判

#include<bits/stdc++.h>
using namespace std;

const double esp = 1e-5;
const int maxn = 2050;
double c[maxn],pp[maxn],dp[2][maxn];

int main(){
    double p1,p2,p3,p4;
    int n,m,k;
    while(scanf("%d%d%d%lf%lf%lf%lf",&n,&m,&k,&p1,&p2,&p3,&p4)!=EOF){
        if(p4<esp){puts("0.00000");continue;}
        double p21=p2/(1-p1),p31=p3/(1-p1),p41=p4/(1-p1);
        pp[0]=1;c[1]=p41;
        for(int i=1;i<=n;i++)pp[i]=pp[i-1]*p21;
        dp[1][1]=p41/(1-p21);
        
        /*
            dp[i][1]=p21*dp[i][i] + p41
            j<=k : dp[i][j]=p21*dp[i][j-1] + p31*dp[i-1][j-1] +p41
            j>k  : dp[i][j]=p21*dp[i][j-1] + p31*dp[i-1][j-1]
            在求dp[i][j]时所有dp[i-1][j]已经求出,所以后面的都是常数
            因为dp[i][1]和dp[i][i]有关,所以需要迭代算方程先求出dp[i][i] 
            c数组是后面的常数,先处理出来即可 
        */
        int cur=1;
        for(int i=2;i<=n;i++){
            cur^=1;
            for(int j=2;j<=k;j++)c[j]=p31*dp[cur^1][j-1]+p41; 
            for(int j=k+1;j<=i;j++)c[j]=p31*dp[cur^1][j-1]; 
            double tmp=c[1]*pp[i-1];//通过迭代方程算出dp[i][i] 
            for(int j=2;j<=i;j++)tmp+=c[j]*pp[i-j]; 
            dp[cur][i]=tmp/(1-pp[i]);
            dp[cur][1]=p21*dp[cur][i]+c[1];//通过递推算出dp[i][1]及其余项 
            for(int j=2;j<i;j++)dp[cur][j]=p21*dp[cur][j-1]+c[j];
        }
        printf("%.5f\n",dp[cur][m]);
    }    
}

 

posted on 2019-06-15 21:32  zsben  阅读(206)  评论(0编辑  收藏  举报

导航