UVa10529 Dumb Bones
原题地址:https://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=1470
题意:
你试图把一些多米诺骨牌排成直线,然后推倒它们。但是如果你在放骨牌的时候不小心把刚放的骨牌碰倒了,它就会把相临的一串骨牌全都碰倒,而你的工作也被部分的破坏了。 比如你已经把骨牌摆成了DD__DxDDD_D的形状,而想要在x这个位置再放一块骨牌。它可能会把左边的一块骨牌或右边的三块骨牌碰倒,而你将不得不重新摆放这些骨牌。 这种失误是无法避免的,但是你可以应用一种特殊的放骨牌方法来使骨牌更多的向一个方向倒下。 给出你要摆放的骨牌数目,以及放骨牌时它向左和向右倒的概率,计算你为完成任务摆放的骨牌数目的平均数。假设你使用了最佳的摆放策略。
输入将包含至多100个测试点,每个测试点占一行,包含需要摆放的骨牌数目n (1≤n≤1000),以及两个非负实数Pl, Pr,表示骨牌向左和向右倒的概率。保证1<Pl+Pr≤0.5。 最后一个测试点包含一个数0。对于每个测试点输出题目要求的数目,保留两位小数。
数据范围
数据组数<=100, 1≤n≤1000 ,1<Pl+Pr≤0.5
题解:
E=El+Er+F(期望再放几次)
=El+Er+(1-pl-pr) * 不倒期望再放几次+pl * 向左倒期望再放几次+pr *向右倒期望再放几次
F= (1-pl-pr) * 不倒期望再放几次+pl * 向左倒期望再放几次+pr *向右倒期望再放几次
我们考虑怎么求 F
向左倒了,我们就要把左边的摆好,然后又回到了已有El ,Er,求E的状况。
此时期望再放几次就是 F
向右边倒同理
F=(1-pl-pr)* 1+pl* (El+F)+pr*(Er+F)
当然我们可以移项 求出F
但是考虑到 F=E-El-Er 我们得到了更简洁的表达形式
即:
F=(1-pl-pr)* 1+pl* (El+E-El-Er)+pr*(Er+F+E-El-Er)
=(1-pl-pr)* 1+pl* (E-Er)+pr*(E-El)
E=El+Er+F
=El+Er+(1-pl-pr)* 1+pl* (E-Er)+pr*(E-El)
把E移项提出来得:
E=((1-pl)* Er + (1-Pr)* El + 1)/(1-pl-pr)
因为每步都采取最优策略
对于每个Ei,我们要枚举所有的k(1<=k<=i),作为放的位置
El=E(k-1) Er=E(i-k)
E=min(((1-pl)*Er + (1-pr)*El + 1)/(1-pl-pr))
至此,得到n^2做法
代码:
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
const int N=1005;
const double inf=0x3f3f3f3f;
int T,n;
double pl,pr,E[N];
double min(double A,double B)
{
return A<B?A:B;
}
int main()
{
scanf("%d",&n);
while(1)
{
if(n==0) break;
scanf("%lf%lf",&pl,&pr);
E[0]=0;
for(int i=1;i<=n;i++)
{
E[i]=inf;
for(int k=1;k<=i;k++)
{
int l=k-1; int r=i-k;
double cur=((1-pl)*E[r]+(1-pr)*E[l]+1.0)/(1-pl-pr);
E[i]=min(E[i],cur);
}
}
printf("%0.2lf\n",E[n]);
scanf("%d",&n);
}
return 0;
}