题意:
你试图把一些多米诺骨牌排成直线,然后推倒它们。但是如果你在放骨牌的时候不小心把刚放的骨牌碰倒了,它就会把相临的一串骨牌全都碰倒,而你的工作也被部分的破坏了。比如你已经把骨牌摆成了DD__DxDDD_D的形状,而想要在x这个位置再放一块骨牌。它可能会把左边的一块骨牌或右边的三块骨牌碰倒,而你将不得不重新摆放这些骨牌。这种失误是无法避免的,但是你可以应用一种特殊的放骨牌方法来使骨牌更多的向一个方向 。
给定n,表示要放n个骨牌,每次放下骨牌,有可能向左倒的概率为pl,向右倒的概率为pr,如果倒下,会将那一侧的骨牌全部推倒,可以选择位置先后放骨牌,问说一种放骨牌次数最少的期望是多少?
题解:
考虑最后放好的多米诺骨牌:一定有一个位置是最后放上去的。对于这个位置两边的位置要放置好,其实是一个子问题,而且没有后效性,因为中间空了一格,左右不会互相影响。可以
DP
求解
现在考虑怎么转移,设
dp[i]
表示放置好长度为
i
的最小期望步数,那么对于长度
关于转移方程,考虑加入最后一个骨牌,把它放置好的期望次数是 st=11−L−R ,其中期望 st∗L 次往左边倒, st∗R 次往右边倒,还要算上在加入这张牌之前的期望步数,那么得出转移:
时间复杂度
O(n2)
。
还有更优秀的做法,比如二分,决策单调性等等,可以参考论文
#include<bits/stdc++.h>
using namespace std;
const int Maxn=1e3+50;
int n;
double L,R,dp[Maxn];
int main(){
while(scanf("%d",&n),n){
scanf("%lf%lf",&L,&R);
dp[0]=0,dp[1]=1.0/(1.0-L-R);
for(int i=2;i<=n;i++){
dp[i]=1e9;
for(int j=0;j<i;j++){
dp[i]=min(dp[i],(dp[j]*(1.0-R)+dp[i-j-1]*(1.0-L)+1.0)/(1.0-L-R));
}
}
printf("%.2f\n",dp[n]);
}
}