【UVA 10529】多米诺骨牌(Dumb Bones)(BSOI4920)

UVA 10529】多米诺骨牌(Dumb Bones)

Description

        你试图把一些多米诺骨牌排成直线,然后推倒它们。但是如果你在放骨牌的时候不小心把刚放的骨牌碰倒了,它就会把相临的一串骨牌全都碰倒,而你的工作也被部分的破坏了。比如你已经把骨牌摆成了DD__DxDDD_D的形状,而想要在x这个位置再放一块骨牌。它可能会把左边的一块骨牌或右边的三块骨牌碰倒,而你将不得不重新摆放这些骨牌。这种失误是无法避免的,但是你可以应用一种特殊的放骨牌方法来使骨牌更多的向一个方向 。
        给定n,表示要放n个骨牌,每次放下骨牌,有可能向左倒的概率为pl,向右倒的概率为pr,如果倒下,会将那一侧的骨牌全部推倒,可以选择位置先后放骨牌,问说一种放骨牌次数最少的期望是多少?

Input

输入一个整数n,2个实数pl,pr。

Output

输出一个实数,放n张骨牌次数最少的期望,结果保留两位小数。

Sample Input

2
0.1  0.1

Sample Output

2.66

Hint

对于100%的数据,2<=n<=10^5,pl+pr<1.0。

Solution

        我们需要注意的是,每一次摆放的左右倾倒概率是一样的,所以说在一个给定长度的序列中,有且仅有一个点,使得在这个点放置最后一块骨牌时,所需的期望次数最小。这一点略微有点像黄金分割,分割出的两条线段各有一个黄金分割点。至于这个多米诺骨牌的分割点,其实是决定与pl和pr的。

        我们很幸运地发现,每一个点的pl和pr是一样的,那么我们的分割点比例总是一样的,就可以用线性的方法递推或者是递归了。对于每一段长度为n的骨牌,我们将其期望设为V[n],放好某一点的期望为E[x],最优的最后放置点为i,那么就有如下表达式:

        v[n]=v[i-1]+E[i]+v[n-i]

              =v[i-1]+(1.0-pl-pr)+pl*(1.0+v[i-1]+E[i])+pr*(1.0+v[n-i]+E[i])+v[n-i]

              =v[i-1]+(pl*v[i-1]+pr*v[n-i]+1)/(1.0-pl-pr)+v[n-i]

至此,题目在O(N)的时间内得解。

CODE

#include<iostream>
#include<cstdio>
using namespace std;
double f[100005];
double pl,pr,ans;
int n;
inline double cal(int n,int i){
	return (pl*f[i-1]+pr*f[n-i]+1)/(1.0-pl-pr)+f[i-1]+f[n-i];//计算期望 
}
int main(){
	cin>>n;
	int i,j,x=1;
	cin>>pl>>pr;
	f[0]=0;f[1]=1.0/(1.0-pl-pr);//初始化,0张牌期望为0,一张牌期望为既不左倒也不右倒的概率 
	for(i=2;i<=n;i++){
		f[i]=cal(i,x);
		while(x<i&&cal(i,x+1)<f[i]){f[i]=cal(i,x+1);x++;}//更优的决策点一定在上一次决策点之后 
	}
	printf("%.2lf",f[n]);//保留两位 
	return 0;
} 

+
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值