一个游戏,你手上有三个骰子,分别有k1, k2, k3面。每次投出这三个骰子,得到三个面x, y, z。并且你有一个计数器,如果投出a, b, c, 则计数器归零,否则计数器加上三面之和,计数器初始为零。如果计数器的值大于 n 则游戏胜利。求胜利所需投骰子次数的期望。
以计数器的值为状态,dp[i] 表述计数器的值为i的情况下投骰子的期望。得到转移方程
p[k] 表示投出点数总和为k的概率,k=0时表示投出计数器归零的概率。
dp[i] = p[0]*dp[0] + Σ(dp[i+k]*p[k]) + 1;
然后dp方程有环,=_=
用待定系数法推,先设:
dp[i] = a[i]*dp[0] + b[i];
带入原方程得
dp[i] = p[0]dp[0] + Σ(p[k](a[i+k]*dp[0]+b[i+k])) + 1;
dp[i] = (p[0]+Σ(p[k]*a[i+k]))*dp[0] + Σ(p[k]*b[i+k]) + 1;
得出:
a[i] = p[0]+Σ(p[k]*a[i+k]);
b[i] = Σ(p[k]*b[i+k]) + 1;
而:
dp[0] = a[0] * dp[0] + b[0];
于是我门根据a[i] 和 b[i] 可以算出:
dp[0] = b[0] / (1 - a[0]);
/***********************************************
** problem ID : zoj_3329.cpp
** create time : Sat Jul 25 15:02:55 2015
** auther name : xuelanghu
** auther blog : blog.csdn.net/xuelanghu407
**********************************************/
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
int n;
int k1, k2, k3, _k;
int a, b, c;
double ans;
double p[20];
void init () {
memset(p, 0, sizeof(p));
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 == a && j == b && k == c) continue;
p[i + j + k] += 1.0 / (k1 * k2 * k3);
}
}
}
}
void solve() {
double A[520];
double B[520];
memset(A, 0, sizeof(A));
memset(B, 0, sizeof(B));
for (int i=n; i>=0; i--) {
A[i] = p[0];
B[i] = 1.0;
for (int j=3; j<=_k; j++) {
A[i] += p[j] * A[i+j] * 1.0;
B[i] += p[j] * B[i+j] * 1.0;
}
}
ans = B[0] * 1.0 / (1.0 - A[0]);
}
int main () {
int T;
cin >> T;
while (T--) {
cin >> n;
cin >> k1 >> k2 >> k3;
cin >> a >> b >> c;
_k = k1 + k2 + k3;
init ();
solve();
printf ("%.15lf\n", ans);
}
return 0;
}
这种成环的dp转移关键在于找到他的待定系数方程。然而并不是都像这题一样那么好推。
版权声明:本文为博主原创文章,未经博主允许不得转载。
时间: 2024-10-14 11:19:37