ZOJ 3329 One Person Game(概率DP+迭代)

来源:互联网 发布:pokemon虚拟定位软件 编辑:程序博客网 时间:2024/06/04 18:32


题意:有三个骰子,分别有k1,k2,k3个面。每次掷骰子,如果三个面分别为a,b,c则分数置0,否则加上三个骰子的分数之和。.当分数大于n时结束。求游戏的期望步数。初始分数为0 。

思路:首先不难想到用dp[i]表示当前步数为i还需要到达目标状态所需要步数的期望,

那么我们可以得到状态转移方程dp(i) = sigma(p[k]*dp(i+k))+dp[0]*p0 + 1.

其中p[k]表示掷出色子为k点的概率,p0表示出现A+B+C的概率。

现在问题来了,因为这样转移我们会得到一个环,最后求不出答案,对于这一类问题,我们一般采取如下做法:

考虑dp[0]为我们所要求的目标,我们的想法是消去sigma(p[k]*dp(i+k))这一项,使得消去原先的环,得到一个线性递推的关系,

为此我们设dp[i] = A[i]*dp[0] + B[i],

那么我们就可以把这个式子代进原始的状态转移方程,得到一个新式子

dp(i) = sigma(p[k]*(A[i+k]*dp[0]+B[i+k]))+dp[0]*p0 + 1.这样我们就消掉了sigma(p[k]*dp(i+k)),进一步化简为

dp(i) = sigma(p[k]*A[i+k]+p0)*dp[0]+ sigma(p[k]*B[i+k])+1

将这个式子与dp[i] = A[i]*dp[0] + B[i]逐项比较,得到

A[i] = p[k]*A[i+k]+p0, B[i] = sigma(p[k]*B[i+k])+1,然后我们就可以进行递推得到A[0],B[0],然后计算得到

dp[0],至此我们就通过反复迭代消去了环得到答案。

#include<cstdio>#include<cstring>#include<cmath>#include<cstdlib>#include<iostream>#include<algorithm>#include<vector>#include<map>#include<queue>#include<stack>#include<string>#include<map>#include<set>#include<ctime>#define eps 1e-6#define LL long long#define pii pair<int, int>//#pragma comment(linker, "/STACK:1024000000,1024000000")using namespace std;const int MAXN = 1000;double A[MAXN], B[MAXN];double p[30], p0;int n, k1, k2, k3, a, b, c;void init() {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++) {p[i+j+k] += p0;}}}p[a+b+c] -= p0;}int main() {    //freopen("input.txt", "r", stdin);int T; cin >> T; while(T--) {scanf("%d%d%d%d%d%d%d", &n, &k1, &k2, &k3, &a, &b, &c);memset(A, 0, sizeof(A));memset(B, 0, sizeof(B));p0 = 1.0/k1/k2/k3;init();for(int i = n; i >= 0; i--) {for(int j = 3; j <= k1+k2+k3; j++) {A[i] += p[j] * A[i+j];B[i] += p[j] * B[i+j];}A[i] += p0; B[i] += 1;}double ans = B[0] / (1-A[0]);printf("%.16f\n", ans);}    return 0;}


0 0
原创粉丝点击