首页 > 其他 > 详细

[ZOJ3329] One Person Game 题解

时间:2020-08-25 14:43:29      阅读:60      评论:0      收藏:0      [点我收藏+]

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\times p_0+\sum_{j=1}^{k1+k2+k3} dp_{i+j} \times p_j \]

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

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

\(~~~~\) 观察式子,若设 \(dp_0\) 为未知数,发现它形似一次函数: \(dp_i=a\times 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\times p_0+\sum_{j=1}^{k1+k2+k3} (a_{i+j}\times dp_0+b_{i+j}) \times p_j \]

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

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

\(~~~~\) 不难发现:

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

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

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

\(~~~~\) 再来考虑答案:\(dp_0=a_0 \times 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\n",(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
*/

[ZOJ3329] One Person Game 题解

原文:https://www.cnblogs.com/Azazel/p/13559041.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!