UVA10529(洛谷)
【题目描述】
你正在尝试用多米诺骨牌搭成一条直线,以便最后试验时推倒它们
(确实,搭建某些东西仅仅为了推倒看上去没啥意义,但你有一些奇怪的爱好)
然而你在搭建过程中可能会弄倒骨牌,这将波及到邻近的部分
现在需要你来求将骨牌搭建完成所需的期望步数
【输入】
若干组数据,以0结尾
一个整数n(1 \leq n \leq 1000)(1≤n≤1000) 表示你需要搭的骨牌数量
两个整数P_l,P_rPl,Pr 分别表示搭建某个骨牌时向左,向右倾倒的概率
【输出】
对于每组数据,输出一个实数,保留两位小数,为搭建完成的期望步数
【样例输入】
10 0.25 0.25
10 0.1 0.4
10 0.0 0.5
0
【样例输出】
46.25
37.28
20.00
分析:
期望是什么?
概率与期望dp+区间dp
题面与区间dp类似。设
E
[
i
]
E[i]
E[i]表示前i块骨牌搭建完成的期望步数。
因此,对于前i块骨牌,可以考虑当前放第j块骨牌,该骨牌左侧骨牌摆好的期望值为
E
[
j
]
E[j]
E[j],右侧为
E
[
i
−
j
−
1
]
E[i-j-1]
E[i−j−1];显然,
E
[
j
]
E[j]
E[j]与
E
[
i
−
j
−
1
]
E[i-j-1]
E[i−j−1]互不影响
当前骨牌没有倒: 1 1 − p l − p r \frac{1}{1-pl-pr} 1−pl−pr1
向左倒: p l ∗ E [ j ] 1 − p l − p r \frac{pl*E[j]}{1-pl-pr} 1−pl−prpl∗E[j]
向右倒: p r ∗ E [ i − j − 1 ] 1 − p l − p r \frac{pr*E[i-j-1]}{1-pl-pr} 1−pl−prpr∗E[i−j−1]
s o , E [ i ] = E [ j ] + E [ i − j − 1 ] + 1 1 − p l − p r + p l ∗ E [ j ] 1 − p l − p r + p r ∗ E [ i − j − 1 ] 1 − p l − p r so,E[i]=E[j]+E[i-j-1]+\frac{1}{1-pl-pr}+\frac{pl*E[j]}{1-pl-pr}+\frac{pr*E[i-j-1]}{1-pl-pr} so,E[i]=E[j]+E[i−j−1]+1−pl−pr1+1−pl−prpl∗E[j]+1−pl−prpr∗E[i−j−1]
E [ i ] = m i n ( 1 1 − p l − p r + ( 1 − p r ) ∗ E [ k ] 1 − p l − p r + ( 1 − p l ) ∗ E [ i − k − 1 ] 1 − p l − p r ) ( 0 < = k < i ) E[i]=min(\frac{1}{1-pl-pr}+\frac{(1-pr)*E[k]}{1-pl-pr}+\frac{(1-pl)*E[i-k-1]}{1-pl-pr})(0<=k<i) E[i]=min(1−pl−pr1+1−pl−pr(1−pr)∗E[k]+1−pl−pr(1−pl)∗E[i−k−1])(0<=k<i)
这样得到一个0(n2)的算法,观察状态转移方程第二项是不断增大的,第三项是减少的,
第一项是常数,则Ei是一个单峰函数,且是下凹的,可以用二分或三分来优化,复杂
度为O(n*logn)
又因为E数组是下凹的,利用这个性质,我们可以证明决策k是不减的,可以利用单调性来优化到O(n)的效率
代码:
#include<bits/stdc++.h>
using namespace std;
const int N=1e3+10;
double f[N],pl,pr;
int n;
double getdp(int i,int j){
return f[j]+f[i-j-1]+(1+pl*f[j]+pr*f[i-j-1])/(1-pl-pr);
}
double solve(){
for(int i=1,j=0;i<=n;i++){
while(j<i-1&&getdp(i,j)>getdp(i,j+1)) j++;
f[i]=getdp(i,j);
}
return f[n];
}
int main(){
while(1){
scanf("%d",&n);
if(n==0) break;
scanf("%lf%lf",&pl,&pr);
printf("%.2lf\n",solve());
}
return 0;
}