zoukankan      html  css  js  c++  java
  • [ZOJ3329] One Person Game 题解

    One Person Game

    题意

    (~~~~) 有三个骰子,分别有 (k_1,k_2,k_3) 个面。 每次掷骰子,如果三个面分别为 (a,b,c) 则分数清零,否则加上三个骰子的点数之和。 当分数达到 (n) 及以上时结束。求游戏结束的期望步数。


    题解

    (~~~~) 期望 (dp) 好题。

    (~~~~) 套路化地,我们设 (dp_i) 表示你有 (i) 分时结束的期望步数。则 (dp_n=0) ,答案为 (dp_0) 。进行倒推。

    (~~~~) 设: (p_i) 表示丢骰子丢出的点数和为 (i) 且能加入分数的概率,特殊地,设 (dp_0) 表示丢出清零的概率。不难发现它等于 (dfrac{1}{k_1k_2k_3}).

    (~~~~) 考虑某个状态可以变为哪些状态,则有转移方程:

    [Large dp_i=1+dp_0 imes p_0+sum_{j=1}^{k1+k2+k3} dp_{i+j} imes p_j ]

    (~~~~) 然后你就会发现转移方程里面有 (dp_0) 项,而倒推时 (dp_0) 恰恰是最后求出来的。

    (~~~~) 那怎么办呢?这题不可做,换一道。

    (~~~~) 观察式子,若设 (dp_0) 为未知数,发现它形似一次函数: (dp_i=a imes dp_{0}+b) .不妨设 (a_i,b_i) 为对应的满足条件的 (a,b).

    (~~~~) 先从特殊情况考虑,当 (i=n) 时,(dp_i=0),因此 (a_i=0,b_i=0)

    (~~~~) 然后我们把原来的转移方程中的 (dp_{i+j}) 也改写一下形式:

    [Large dp_i=1+dp_0 imes p_0+sum_{j=1}^{k1+k2+k3} (a_{i+j} imes dp_0+b_{i+j}) imes p_j ]

    (~~~~) 再稍稍展开一下:

    [Large dp_i=dp_0 imes (p_0+sum_{j=1}^{k_1+k_2+k_3}a_{i+j} imes p_j) +1+sum_{j=1}^{k_2+k_2+k_3} b_{i+j} imes p_j ]

    (~~~~) 不难发现:

    [Large a_i=p_0+sum_{j=1}^{k_1+k_2+k_3}a_{i+j} imes p_j ]

    [Large b_i=1+sum_{j=1}^{k_2+k_2+k_3} b_{i+j} imes p_j ]

    (~~~~) 因此这样转移 (a,b) 即可。

    (~~~~) 再来考虑答案:(dp_0=a_0 imes dp_0+b_0) ,因此 (dp_0=dfrac{b_0}{1-a_0}),即答案。


    代码

    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    using namespace std;
    double p[20];
    double A[505],B[505];
    int main() {
    	int T,n,k1,k2,k3,a,b,c;
    	scanf("%d",&T);
    	while(T--)
    	{
    		scanf("%d %d %d %d %d %d %d",&n,&k1,&k2,&k3,&a,&b,&c);
    		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;
    				}
    			}
    		}
    		for(int i=3;i<=k1+k2+k3;i++) p[i]=p[i]/(double)(k1*k2*k3);
    		for(int i=n;i>=0;i--)
    		{
    			A[i]=p[0];B[i]=1.0;
    			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];
    			}
    		}
    		printf("%.15f
    ",(double)B[0]/(1.0-A[0]));
    		memset(A,0,sizeof(A));
    		memset(B,0,sizeof(B));
    		memset(p,0,sizeof(p));
    	}
    	return 0;
    }
    /*
    2
    0 2 2 2 1 1 1
    0 6 6 6 1 1 1
    */
    
  • 相关阅读:
    socket上传nsdictionary的json数据异常
    Eclipse中如何关联Javadoc
    《算法导论》读书笔记之第8章 线性时间排序
    《算法导论》读书笔记之第6章 堆排序
    《算法导论》读书笔记之第3章 函数的增长
    《算法导论》读书笔记之第2章 算法入门
    《算法导论》读书笔记之第7章 快速排序
    堆排序
    八皇后
    行指针的理解
  • 原文地址:https://www.cnblogs.com/Azazel/p/13559041.html
Copyright © 2011-2022 走看看