[ZOJ3329] One Person Game

传送门:>出错啦<

题意:有三个骰子,每个骰子分别有k1,k2,k3面,若三个骰子分别骰到a, b, c则计数器归0,否则计数器加上三个骰子得到的数之和。求计数器达到n及以上所需要骰的次数的期望。

解题思路:

  继续期望DP。

  f[i]表示从i分到达n分所需要骰的次数的期望,因此f[n]=0 答案为f[0]

  设骰一次(三个骰子之和)骰到k分的概率为p[k]p[0]的概率即为骰到a,b,c的概率,只有1k1k2k3。另外的累积一下可能性,也除以总可能性就可以了。

  由i分转移到i+j分,分别加上相应的概率。并且还要特殊处理计数器清0的情况:清零的可能性乘以0分的期望。于是我们很容易得到方程:f[i]=j=1 j!=a+b+ck1+k2+k3(p[j]f[i+j])+f[0]p[0]+1

  但是注意,f[0]是我们要求的答案,怎么反而用来转移了?所以这个方程很不可行……

  但是在这个递推式中,除了f[0]之外都是已知的,所以我们可以把它看做一个关于f[0]一次函数:f[i]=kf[0]+b

  并且我们发现系数只与i有关,所以 f[i]=A[i]f[0]+B[i]

  因此可以替换f[i+j]f[i]=j=1 j!=a+b+ck1+k2+k3(p[j](A[i+j]f[0]+B[i+j]))+f[0]p[0]+1

  因此A[i]=A[i+j]p[j]+p[0]B[i]=B[i+j]p[j]+1

  特殊的,当i==0时:f[0]=A[0]f[0]+B[0]

  也就是f[0]=B[0]1A[0]

  这就是答案

Code

  不要先做完再memset了

 

复制代码
/*By QiXingzhi*/
#include <cstdio>
#include <queue>
#define  r  read()
#define  Max(a,b)  (((a)>(b)) ? (a) : (b))
#define  Min(a,b)  (((a)<(b)) ? (a) : (b))
using namespace std;
typedef long long ll;
const int N = 1010;
const int INF = 1061109567;
inline int read(){
    int x = 0; int w = 1; register int c = getchar();
    while(c ^ '-' && (c < '0' || c > '9')) c = getchar();
    if(c == '-') w = -1, c = getchar();
    while(c >= '0' && c <= '9') x = (x << 3) +(x << 1) + c - '0', c = getchar();
    return x * w;
}
int n,a,b,c,k1,k2,k3,T;
double p[N],A[N],B[N];
inline void Solve(){
    p[0] = (double)1.0 / (double)(k1*k2*k3);
    for(int k = 3; k <= k1 + k2 + k3; ++k) p[k] = 0.0;
//    printf("p[0] = %.5lf\n",p[0]);
    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;
//                printf("P[%d] = %d\n",i+j+k,P[i+j+k]);
            }
        }
    }
    for(int k = 3; k <= k1+k2+k3; ++k){
        p[k] = (double)(p[k]) / (double)(k1*k2*k3);
    }
    for(int i = n; i >= 0; --i){
        A[i] = 0.0;
        B[i] = 0.0;
    }
    for(int i = n; i >= 0; --i){
        for(int j = 3; j <= k1+k2+k3; ++j){
            if(i + j > n) continue;
            A[i] += A[i+j] * p[j];
            B[i] += B[i+j] * p[j];
        }
        A[i] += p[0];
        B[i] += 1.0;
//        printf("A[%d] = %.5lf  B[%d] = %.5lf\n",i,A[i],i,B[i]);
    }
    printf("%.15lf\n", (double)(B[0]) / (double)(1.0 - A[0]));
}
main(){
//    freopen(".in","r",stdin);
    T = r;a
    while(T--){
        n = r;
        k1 = r, k2 = r, k3 = r;
        a = r, b = r, c = r;
        Solve();
    }
    return 0;
}
复制代码

 

posted @   行而上  阅读(326)  评论(1编辑  收藏  举报
编辑推荐:
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
点击右上角即可分享
微信分享提示