P1409 骰子 题解(评分:7.7)(2022.3.16)

前言

先科普,“骰”字读

tóu not shai

Solution

看到这道题的题面,我的思路异常清晰:

动态规划啊!

然后咻咻咻的一下,状态转移方程就来了,很快啊。

状态转移方程

\(f_{n,m}\) 表示有 \(n\) 个人的情况下站在第 \(m\) 位的胜率,则可以分两种情况讨论:

1、\(f_{n,1}\) (即我在第一位)

因为有 \(\frac{1}{2}\) 的概率排到队尾,\(\frac{1}{6}\) 的概率直接胜利,所以可以得出:

\(f_{n,1}=\frac{1}{6}+\frac{f_{n,1}}{2}\)

2、\(f_{n,m},(m \ne 1)\) (即我不在第一位)

由于有 \(\frac{1}{3}\) 的概率当前排在第一位的人直接去世(我的排位会向前一位),\(\frac{1}{2}\) 的概率当前排在第一位的人来到最后(也就是我向前走了一位),所以说可以得出:

\(f_{n,m}=\frac{f_{n-1,m-1}}{3}+\frac{f_{n,m-1}}{2},(m \ne 1)\)

好啦,状态转移方程已经写完了。然后呢?

然后就出现嵌套啦!

3、解决嵌套问题

观察状态转移方程,我们不难发现,有 \(\frac{1}{2^{n}}\) 的概率计算 \(f_{n,1}\) 时会重新回到 \(f_{n,1}\)

假设用 \(g_n\) 来表示不考虑嵌套部分(也就是递归函数第二次到达计算 \(f_{n,1}\) 时直接返回 \(0\))时 \(f_{n,1}\) 的值(这个值本身不是真正的 \(f_{n,1}\)),那我们就可以得出这么一个玩意儿:

\(f_{n,1}=g_n+\frac{f_{n,1}}{2^n}\)

解出这个方程,可得:

\(f_{n,1}=\frac{2^n}{2^n-1}g_n\)

计算方式

\(g_n\) 可以用递归在 \(O(n)\) 之内算出,算出 \(g_n\) 就可以得到 \(f_{n,1}\),也就可以接着用状态转移方程算出 \(f_{n,m}\)

另外由于计算 \(f_{n,m},(m \ne 1)\) 时调用了 \(f_{n-1,m-1}\),所以最后需要从 \(f_{2,m}\) 一直算到 \(f_{1000,m}\),边界是 \(f_{1,1}=1\)。(注意:刚刚这两段所说的 \(m\) 表示范围里面的所有数)

然后输出 \(f_{n,m}\) 即可。总复杂度 \(O(n^2)\),能过。

AC代码

#include<bits/stdc++.h>
#define d double
using namespace std;
int m,n,i,j,k;d f[1145][1145];
d ans(int x,d y)
{
	if(x==1)
	{
		if(y!=1.0)
		{
			return 0.0;
		}
		return (ans(i,1/2.0)+1/6.0)*1/(1-pow(0.5,i));
	}
	return (f[i-1][x-1]/3.0+ans(x-1,y/2.0))/2.0;
}
int main()
{
	cin>>n>>m;f[1][1]=1.0;
	for(i=2;i<=1000;i++)
	{
		f[i][1]=ans(1,1.0);
		for(j=2;j<=i;j++)
		{
			f[i][j]=f[i-1][j-1]/3.0+f[i][j-1]/2.0;
		}
	}
	printf("%.9f",f[n][m]);
	return 0;
}
posted @ 2024-04-17 08:07  Fun_Strawberry  阅读(31)  评论(0)    收藏  举报