[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\)
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;
}