ZOJ 3582 Back to the Past [概率DP]

Description

Recently poet Mr. po encountered a serious problem, rumor said some of his early poems are written by others. This brought a lot of trouble to Mr. po, so one day he went to his best friend MasterO for help. MasterO handed over a small wooden box with a silent smile. Mr. po checked the box, a word "YGBH" carved on the side. "The box can take you back to the past," MasterO said, "so you can get any evidence you need. But, before that, you need some patience."

There are N tiny dark holes on both sides of the box (2N holes in total). Every day, for each hole, there is a possibility P to successfully absorb the power of moon and then magically sparkle. The possibilities among holes are independent in each day. Once a hole sparkles, it will never turn dark again. The box only works when there are no less than M sparkling holes on each side of the box. The question is that what is the expected number of days before the box is available.

Input

The input consists of several test cases. For each case there are 3 numbers in a line: NMP. 1 ≤ N ≤ 50, 1 ≤ M ≤ N, 0.01 ≤ P ≤ 1. A case with three zeros indicates the end of the input.

Output

For each test case, output the expected number of days, accurate up to six decimal places.

题意:

现有一月光宝盒,盒子上两面各有孔N枚,一开始孔都是暗的,每一天每个孔都有等概率P的机会亮起(已亮起的不会再熄灭),当两面亮起的孔各不少于M个,就会回到过去,问你回到过去前所需要的天数的期望。

范围:

N,M<=50

解法:

概率DP,设DP[I][J]为一面亮起I个孔,另一面亮起J个孔时,还需要的天数期望,可知DP[I>=M][J>=M]=0

分析可得,对于第一面来说,剩下N-I个孔,亮起K个的概率为C(N-I,K)*p^K *(1-P)^(N-I-K)

那么DP[I][J]=ΣΣ (DP[I+K][J+L]*P(I,L)) +1 +P(0,0)*DP[I][J]

其中 K,L不同时等于0,P(a,b)表示这一天第一面亮起a个,且第二面亮起b个孔的概率,计算方法为将各自亮起的概率(公式如前)相乘(因为满足乘法原则,相互独立且同时发生)

化掉右边的DP[I][J]得

DP[I][J]=( ΣΣ (DP[I+K][J+L]*P(I,L)) +1 )/(1-P(0,0))

时间复杂度为O(N^4)

代码:

实现DP用了记忆化搜索

#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<math.h>
#include<iostream>
#include<stdlib.h>
#include<set>
#include<map>
#include<queue>
#include<vector>
#include<bitset>
#pragma comment(linker, "/STACK:1024000000,1024000000")
template <class T>
bool scanff(T &ret){ //Faster Input
    char c; int sgn; T bit=0.1;
    if(c=getchar(),c==EOF) return 0;
    while(c!='-'&&c!='.'&&(c<'0'||c>'9')) c=getchar();
    sgn=(c=='-')?-1:1;
    ret=(c=='-')?0:(c-'0');
    while(c=getchar(),c>='0'&&c<='9') ret=ret*10+(c-'0');
    if(c==' '||c=='\n'){ ret*=sgn; return 1; }
    while(c=getchar(),c>='0'&&c<='9') ret+=(c-'0')*bit,bit/=10;
    ret*=sgn;
    return 1;
}
#define inf 1073741823
#define llinf 4611686018427387903LL
#define PI acos(-1.0)
#define lth (th<<1)
#define rth (th<<1|1)
#define rep(i,a,b) for(int i=int(a);i<=int(b);i++)
#define drep(i,a,b) for(int i=int(a);i>=int(b);i--)
#define gson(i,root) for(int i=ptx[root];~i;i=ed[i].next)
#define tdata int testnum;scanff(testnum);for(int cas=1;cas<=testnum;cas++)
#define mem(x,val) memset(x,val,sizeof(x))
#define mkp(a,b) make_pair(a,b)
#define findx(x) lower_bound(b+1,b+1+bn,x)-b
#define pb(x) push_back(x)
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;

double dp[55][55];
bool vis[55][55];
int n,m;
double p,px[111],p1[111];
ll c[55][55];
void init(){
    px[0]=p1[0]=1.0;
    rep(i,1,100){
        px[i]=px[i-1]*p;
        p1[i]=p1[i-1]*(1.0-p);
    }
    rep(i,0,50)c[i][0]=1;
    rep(i,1,50)
    rep(j,1,i){
        c[i][j]=c[i-1][j-1]+c[i-1][j];
    }

}
double dfs(int a,int b){
    if(a>=m&&b>=m)return 0.0;
    if(vis[a][b])return dp[a][b];
    vis[a][b]=1;
    double p0=double(c[n-a][0])*px[0]*double(c[n-b][0])*p1[n-a+n-b];
    dp[a][b]=1.0/(1.0-p0);
    rep(i,0,n-a){
        rep(j,0,n-b){
            if(i==0&&j==0)continue;
            double f=double(c[n-a][i])*px[i+j]*double(c[n-b][j])*p1[n-a-i+n-b-j]/(1.0-p0);
            dp[a][b]+=f*dfs(a+i,b+j);
        }
    }
    return dp[a][b];
}
int main(){
    while(scanf("%d%d%lf",&n,&m,&p)!=EOF){
        if(n==0||m==0)break;
        init();
        mem(vis,0);
        mem(dp,0);
        double ans=dfs(0,0);
        printf("%.6lf\n",ans);
    }
    return 0;
}
/*
2
500 2 2 2 1 1 1
*/






發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章