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;
}

浙公网安备 33010602011771号