UVA10529 Dumb Bones
链接:
题意:
你需要搭建长度为 \(n\) 的多米诺骨牌,骨牌在搭建时可能会向左或向右倒,这将让连续的多个相邻骨牌都被弄倒,现在给出 \(n\) 和向左和向右倒的概率,求期望搭多少次才能搭完。
分析:
这道题没有思路的时候会感觉很杂乱。
我们考虑要搭建一个区间,长度为 \(k\),似乎有很多搭建的方法,但注意到最后的一次搭建时,两边一定都是搭建好的,也就是说枚举最后一次搭建的位置,然后转移就是
\[f[l][r]=f[l][k-1]+f[k+1][r]+\frac{1}{1-pl-pr}+\frac{pl}{1-pl-pr}*f[l][k-1]+\frac{pr}{1-pl-pr}*f[k+1][r]
\]
解释一下:
成功搭建的概率是 \(1-pl-pr\),那么搭建成功的期望次数是 \(\dfrac{1}{1-pl-pr}\),这是显然的,然后搭建成功之前都是搭建失败的,总失败数是 \(\dfrac{1}{1-pl-pr}-1=\dfrac{pl+pr}{1-pl-pr}\),在根据向左和向右的概率得出重搭左边的次数是 \(\dfrac{pl}{1-pl-pr}\),重搭右边的次数是 \(\dfrac{pr}{1-pl-pr}\),于是就有状态转移方程了。
因为 \(pl,pr\) 对于每个骨牌都是相同的,所以两个长度相同的区间搭建次数显然是完全相同的,所以我们可以把二维区间dp改成一维,只需要设 \(f[i]\) 表示搭建长度为 \(i\) 的区间的期望次数就好了,这样就可以从 \(O(n^3)\) 优化到 \(O(n^2)\)。另外注意到题目要求是选择最好的策略,所以转移的时候要取min。
算法:
根据转移方程区间dp即可。
代码:
#include<bits/stdc++.h>
using namespace std;
int n,T;
double pl,pr,f[1005][1005];
signed main(){
memset(f,127,sizeof(f));
while(cin>>n&&n){
cin>>pl>>pr;T++;
f[T][1]=1.0/(1.0-pl-pr);
f[T][0]=0;
for(int i=2;i<=n;i++)
for(int j=1;j<=i;j++)
f[T][i]=min(f[T][i],1.0/(1.0-pl-pr)+f[T][j-1]*(1+pl/(1.0-pl-pr))+f[T][i-j]*(1+pr/(1.0-pl-pr)));
cout<<fixed<<setprecision(2)<<f[T][n]<<'\n';
}
return 0;
}
题外话:
神奇的概率题,抓住最后的一次搭建时的特殊情况来dp。

浙公网安备 33010602011771号