ZJU 3582 Back to the Past

http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=4624
DP,期望

3582

       一个东东上下各有n个灯,灯有亮和暗两种状态,起初都是暗的,每天每盏灯亮的概率是p,各个灯独立并且一个灯亮了就不会暗掉,问上下各至少有m盏灯亮的期望是多少天。

       设e(x,y)为上面亮了x盏灯,下面亮了y盏灯的期望。

       则枚举本天灯亮的状态:

       e(x,y)=1+sigma(e(x+i,y+j)*C(n-x,i)*C(n-y,j)*p^(i+j)*(1-p)^(n-x-i+n-y-j))  

其中给你i是从0到n-x,j是从0到n-y。

注意等号右边也有e(x,y) (在i=j=0时),要移项一下。

然后可以递归之。边界条件是e(x,y)=0 if x>=m && y>=m

重复的不要重新算,并且e(x,y)=e(y.x) 用对称性可以少算一半状态。



#include<cstdio>
#include<cstring>
#include<iostream>
#define prt(k) cout<<#k"="<<k<<endl;
using namespace std;
#define ll long long
ll c[222][112];

int n,m;
double e[222][211];
double P, p[222], p2[222];
void init()
{
    memset(c,0,sizeof c);
    for(int i=0;i<66;i++) c[i][0]=c[i][i]=1;
    for(int i=0;i<55;i++)
    {
        for(int j=0;j<i;j++)
            c[i][j]=c[i-1][j-1]+c[i-1][j];
    }
   // prt(c[6][2])
    p[0]=p2[0]=1.0;
    for(int i=1;i<88;i++)
    {
        p[i]=p[i-1]*P;
        p2[i]=p2[i-1]*(1-P);
    }
    memset(e,0,sizeof e);
}
double get(int x,int y,int i,int j)
{
    return c[n-x][i]*p[i]*p2[n-i-x]*c[n-y][j]*p[j]*p2[n-y-j];
}
double dfs(int x,int y)
{
    if(x>=m&&y>=m) return 0;
    if(e[x][y]>1e-8) return e[x][y];
    e[x][y]=1.;
    for(int i=0;i<=n-x;i++)
        for(int j=0;j<=n-y;j++)
    {
        if(i==0&&j==0) continue;
        e[x][y]+=get(x,y,i,j)*dfs(x+i,y+j);
    }
    return e[x][y]/=(1-get(x,y,0,0));
}
int main()
{
    while(cin>>n>>m>>P&&n)
    {
        init();
        printf("%.6f\n",dfs(0,0));  //cout<<dfs(0,0)<<endl;
    }
}


你可能感兴趣的:(ZJU 3582 Back to the Past)