题意有三个骰子,分别有k1,k2,k3面,每个面等概率,现在同时投三个骰子,如果第一个为a,第二个为b,第三个为c,直接将总和置为0,否则就在总和上加上三个骰子的和,问要使总和大于n要多少轮的期望为多少。
看了kuangbin的题解,明白了很多。
定义dp[i] 当前总和为 i 到结束的期望次数。
dp[i]=∑(pk*dp[i+k])+dp[0]*p0+1;
你发现这个问题变成了一个环,递归下去,没办法解决。
但是,因为每次都是只和dp[0]有关,所以可以把系数分解出来。
那么就有了 dp[i] = A[i]*dp[0] + B[i]
然后再代入原式,得到A[i] 和 B[i],发现 A, B都是可以dp求解的,最后dp[0]也就求出来了。
/*************************************************************************
> File Name: source.cpp
> Author: oldflag
> Created Time: 星期一 10/ 3 22:27:45 2016
************************************************************************/
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
#include <set>
#include <map>
#include <cmath>
#include <cstdlib>
#include <stack>
#include <queue>
using namespace std;
typedef long long LL;
int k1, k2, k3;
int a, b, c;
int sum;
int n;
double p[37];
double p0;
double A[550];
double B[550];
int main()
{
// freopen("data.vim", "r", stdin);
int T;
scanf("%d", &T);
while(T--)
{
scanf("%d", &n);
scanf("%d%d%d", &k1, &k2, &k3);
scanf("%d%d%d", &a, &b, &c);
sum = k1 + k2 + k3;
p0 = 1.0/k1/k2/k3;
memset(p, 0, sizeof p);
memset(A, 0, sizeof A);
memset(B, 0, sizeof B);
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) continue;
p[i + j + k] += p0;
}
}
}
for(int i = n; i >= 0; i--)
{
A[i] = p0;
B[i] = 1;
for(int j = 3; j <= sum; j++)
{
A[i] += p[j]*A[i + j];
B[i] += p[j]*B[i + j];
}
}
printf("%.16lf\n", B[0]/(1 - A[0]));
}
return 0;
}