UVA10529 Dumb Bones

链接:

UVA10529


题意:

你需要搭建长度为 \(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。

posted @ 2021-11-05 16:17  llmmkk  阅读(102)  评论(0)    收藏  举报