luogu UVA10529 Dumb Bones
有没有跟我一样翻完题解对着状态转移方程一脸懵 * 的蒟蒻。/kk
设\(f[i]\)表示搭好\(i\)个连续的骨牌的期望次数,由于两段不连续的区间肯定互不影响,所以我们可以考虑枚举最后一个放的位置,根据期望的线性性质进行累加。
例如:\(1 \ 2 \ ? \ 4 \ 5\)
假设\(?\)位置是我们最后一个放骨牌的位置,那么显然我们在摆放\(1 \ 2\)的时候不会影响到\(4\ 5\),同理摆放\(4\ 5\)的时候也不会影响到\(1\ 2\)。
对于每个\(i\),我们枚举一个\(j\)表示最后一个放的位置,状态转移如下:
\(f[i]=min( \frac{1-pr}{1-pl-pr}\times f[j-1] + \frac{1-pl}{1-pl-pr}\times f[i-j] )+ \frac{1}{1-pl-pr}\)
如何理解这个转移呢?
- 以下为一个插曲:
@Binary_Search_Tree 神仙前几天给了我一个结论:某事件\(A\)第一次发生的期望次数 \(=\frac{1}{P(A)}\)(其中\(P(A)\)表示\(A\)发生的概率)
因为萌新初学期望之前也没听说这个东西,如果要证明的话我只能说你随便举个例子然后用期望的定义自己推一下(逃)。
再给一个 知乎上的栗子(可以直接看他后面通俗的解释)
- 回到正题,我们来解释这个转移方程:
首先看转移方程后面的那个\(\frac{1}{1-pl-pr}\):我们单独考虑最后你要放的那个骨牌的期望次数,发现你有\(1-pl-pr\)的概率不倒,直接套用上面结论即可。
然后我们拿最后摆放位置的左边为例解释一下\(\frac{1-pr}{1-pl-pr}\times f[j-1]\)(右边同理):
根据上面我们知道在最后一张骨牌放好前那张牌总共会倒\(\frac{1}{1-pl-pr}-1\)次(因为最后一次他就放好了所以要减一个\(1\)),整理一下也就是\(\frac {pl+pr}{1-pl-pr}\)次,注意这是总共会倒的次数,那么向左倒的次数就应该乘一个\(\frac{pl}{pl+pr}\),所以向左倒的次数为:
,由于\(j\)是我们最后一个摆的位置,也就是之前我们还会把左边的牌先摆一遍,所以你摆左边这些牌的总次数为\(\frac{pl}{1-pl-pr}+1= \frac{1-pr}{1-pl-pr}\),那么左边这些牌对\(f[i]\)的贡献显然就是
了吧(终于推完了),最后每个位置去个\(min\)就完事了。
时间复杂度:\(O(n^2)\)
至于\(O(n)\)的做法嘛,转移方程的理解最重要嘛,所以就不讲了,优化什么优化,能过就行。
这是初学期望的萌新对这个题的理解,如果有非常简单的对转移方程的理解欢迎在评论区或者私信打脸。
代码:
#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=1009;
int n;
double pl,pr,f[N];
void work()
{
scanf("%d",&n);
while(n)
{
scanf("%lf %lf",&pl,&pr);
f[0]=0,f[1]=1/(1-pl-pr);
for (int i=2;i<=n;i++)
{
f[i]=1<<30;
for (int j=1;j<=i;j++)
f[i]=min(f[i],(1-pr)/(1-pr-pl)*f[j-1]+(1-pl)/(1-pr-pl)*f[i-j]);
f[i]+=1/(1-pl-pr);
}
printf("%.2lf\n",f[n]);
scanf("%d",&n);
}
}
int main()
{
work();
return 0;
}