ZOJ 3329 One Person Game

思路

典型的一类概率dp问题
逆推,用dp[i]表示从n到i的期望次数,p[i]表示获得i分数的概率,px表示分数清零的概率
容易想到题目中的转移方程为\(dp[j]=\sum_{i}^{sumk}dp[j+i]p[i]+dp[0]px+1\)
显然从dp[n]开始,要求dp[0]
可是每个状态的转移都包含了dp[0],转移出现环了怎么办?
高斯消元?可是n=500,T=300,根本过不去
发现并不每个都互相依赖,而是都依赖于dp[0],那我们找出dp[x]与dp[0]的关系,问题似乎就可以解决了
由于期望的线性性,我们尝试列出一个线性的方程组的形式(或者说就是给dp[0]配个系数)
\[ dp[x]=a_xdp[0]+b_x \]
把它带入转移方程中,得到
\[ dp[j]=\sum_{i}^{sumk}(a_{i+j}dp[0]+b_{i+j})p[i]+dp[0]px+1 \]
尝试把它变形回原来的形式
\[ dp[j]=dp[0](px+\sum_{i}^{sumk}a_{i+j}p[i])+\sum_{i}^{sumk}b_{i+j}+1 \]
把他和原式\(dp[x]=a_xdp[0]+b_x\)对应,得到
\[ a[j]=(px+\sum_{i}^{sumk}a_{i+j}p[i]) \]
\[ b[j]=1+\sum_{i}^{sumk}b_{i+j} \]
这样a和b就可以逆推出来了
然后dp[0]就等于b[0]/(1-a[0])
然后就好了

代码

#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
int T,n,k1,k2,k3,ax,bx,cx;
double p[700],P,a[700],b[700];
int main(){
    scanf("%d",&T);
    while(T--){
        memset(p,0,sizeof(p));
        memset(a,0,sizeof(a));
        memset(b,0,sizeof(b));
        scanf("%d %d %d %d %d %d %d",&n,&k1,&k2,&k3,&ax,&bx,&cx);
        P=1.0/(k1*k2*k3);
        for(int i=1;i<=k1;i++)
            for(int j=1;j<=k2;j++)
                for(int k=1;k<=k3;k++)
                    if(i!=ax||j!=bx||k!=cx)
                        p[i+j+k]+=P;
        for(int i=n;i>=0;i--){
            a[i]=P,b[i]=1;
            for(int k=0;k<=k1+k2+k3;k++)
                a[i]+=a[i+k]*p[k],b[i]+=b[i+k]*p[k];
        }
        double ans=b[0]/(1-a[0]);
        printf("%.15lf\n",ans);
    }
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/dreagonm/p/10527521.html
今日推荐