先记录一下这题怎么做……等做多了再归纳总结一下,这个先留个坑……
题意:给定三个骰子,分别有k1、k2、k3三个面,1<K1,K2,K3<=6,每次计数从0开始,当掷得的三个骰子各为a、b、c时,计数归零,否则计数加上三个骰子的数之和,计数大于等于n时停止,求需要掷的次数的期望
思路:设dp[i]为当前计数为i到目标所需要的次数的期望,那么可以很容易得到:
dp[i]=Σdp[i+k]*pk+dp[0]*p0+1 ————————————(1)
pk为掷得的点数和为k的概率,p0为正好掷得a、b、c的概率,实际就是1/(k1*k2*k3),我们知道dp[n]、dp[n+1]...都等于0,想逆向求得dp[i],可是有dp[0]这个未知量,但dp[0]又正好是我们需要求的量,可以说dp[i]都与dp[0]有关,我们可以设:
dp[i]=A[i]dp[0]+B[i] ————————————(2),那么带入(1)式,可以得到:
dp[i]=Σ(A[i+k]*dp[0]*pk+B[i+k]*pk)+dp[0]*p0+1
=(ΣA[i+k]*pk+p0)*dp[0]+ΣB[i+k]*pk+1———————————(3)
(2)式和(3)式对应相等,那么:
A[i]=ΣA[i+k]*pk+p0
B[i]=ΣB[i+k]*pk+1
这两个式子都是不带未知量的递推公式,我们可以事先预处理得到pk,然后逆推可以依次得到A[i]、B[i],最终得到A[0]、B[0],那么dp[0]=B[0]/(1-A[0])
我在想这是不是一个通用的方法目的是把未知量dp[0]消去,然后就能求递推式了,现在还不好下结论,毕竟还是做的题少……
完整代码:
#include<cstring>
#include<cmath>
#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long ll;
const int N=605;
double A[N],B[N];
double p[100];
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
int n,k1,k2,k3,a,b,c;
scanf("%d%d%d%d%d%d%d",&n,&k1,&k2,&k3,&a,&b,&c);
double tmp=1.0/(k1*k2*k3);
memset(p,0,sizeof(p));
for(int i=1;i<=k1;i++)
for(int j=1;j<=k2;j++)
for(int k=1;k<=k3;k++)
if(!(i==a&&j==b&&k==c))
p[i+j+k]+=tmp;
memset(A,0,sizeof(A));
memset(B,0,sizeof(B));
for(int i=n;i>=0;i--)
{
A[i]=tmp;
B[i]=1;
for(int j=1;j<=k1+k2+k3;j++)
{
A[i]+=(A[i+j]*p[j]);
B[i]+=(B[i+j]*p[j]);
}
}
printf("%.16lf\n",B[0]/(1-A[0]));
}
return 0;
}