1. 程式人生 > 其它 >[ZOJ3329]One Person Game 題解

[ZOJ3329]One Person Game 題解

期望 DP+解係數 Trick

Statement

有三個色子,第 \(i\) 個色子有 \(k_i\) 面,每面面值為 \(1,2, … ,k_i\) ,每面出現的概率相同

每次扔出三個色子,將面值加入總得分中。

問期望扔多少次使得總得分大於 \(n\)

額外規定: 當 \(k1=a, k2 = b, k3 =c\) 時,總得分清零。

多組資料。

\(1 ≤ T≤ 300, 0 ≤n≤ 500,1 < k1,k2, k3 ≤ 6\)

ZOJ (pintia.cn)

Solution

容易地,設 \(dp[i]\) 表示當前得分 \(i\) 到目標狀態的期望次數

容易 \(k^3\) 預處理出 \(p[k]\)

表示一次扔出得分 \(k\) 的概率

那麼 \(dp[i]=\sum (dp[i+k]\times p[k])+dp[0]\times p[0]\)

發現每一個式子裡面都有 \(dp[0]\) 而且答案就是 \(dp[0]\)

高消時間炸糊了,這裡給出一個 nb 的 Trick:

考慮設 \(dp[i]=A[i]\times dp[0]+B[i]\)

那麼 \(dp[i]=\sum p[k](A[i+k]\times dp[0]+B[i+k])+dp[0]\times p[0]+1\)

所以 \(dp[i]=(\sum (p[k]\times A[i+k])+p[0])dp[0]+\sum(p[k]\times B[i+k])+1\)

所以 \(A[i]=\sum (p[k]\times A[i+k])+p[0],B[i]=\sum(p[k]\times B[i+k])+1\)

你發現 \(A,B\) 的遞推式沒有後效性!所以就可以做了

Code

#include<bits/stdc++.h>
using namespace std;
const int N = 3e5+5;
const int inf = 2e9;

char buf[1<<23],*p1=buf,*p2=buf;
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++)
int read(){
    int s=0,w=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
    while(ch>='0'&&ch<='9')s=s*10+ch-'0',ch=getchar();
    return s*w;
}

double a[1000],b[1000],p[100];
int T,n,k1,k2,k3,x,y,z;

signed main(){
    T=read();
    while(T--){
        memset(a,0,sizeof(a)),memset(b,0,sizeof(b)),memset(p,0,sizeof(p));
        n=read(),k1=read(),k2=read(),k3=read(),x=read(),y=read(),z=read();
        p[0]=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==x&&j==y&&k==z))
                p[i+j+k]+=p[0];
        for(int i=n;~i;--i){
            a[i]=p[0],b[i]=1;
            for(int k=3;k<=k1+k2+k3;++k)
                a[i]+=p[k]*a[i+k],b[i]+=p[k]*b[i+k];
        }
        printf("%.15lf\n",b[0]/(1.0-a[0]));
    }
    return 0;
}