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
*/






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