[UVA]10529 Dumb Bones 期望 + 区间DP DP函数单调性优化

题意:你试图把一些多米诺骨牌排成直线,然后推倒它们。但是如果你在放骨牌的时候不小心把刚放的骨牌碰倒了,它就会把相临的一串骨牌全都碰倒,而你的工作也被部分的破坏了。 比如你已经把骨牌摆成了DD__DxDDD_D的形状,而想要在x这个位置再放一块骨牌。它可能会把左边的一块骨牌或右边的三块骨牌碰倒,而你将不得不重新摆放这些骨牌。 这种失误是无法避免的,但是你可以应用一种特殊的放骨牌方法来使骨牌更多的向一个方向倒下。 给出你要摆放的骨牌数目,以及放骨牌时它向左和向右倒的概率,计算你为完成任务摆放的骨牌数目的平均数。假设你使用了最佳的摆放策略。 输入将包含至多100个测试点,每个测试点占一行,包含需要摆放的骨牌数目n (1≤n≤1000),以及两个非负实数Pl, Pr,表示骨牌向左和向右倒的概率。保证1<Pl+Pr≤0.5。 最后一个测试点包含一个数0。对于每个测试点输出题目要求的数目,保留两位小数。

期望大多数都是倒推, 这道题也不例外. 但是期望一直不熟, 这次也推了好久发现是错误的…其实是没有get到倒推里的末状态这个点. 末状态是全部摆好, 那么这也就意味着左边和右边都放好了, 最后一块骨牌放在中间没有倒. 我们设左边和右边已经摆好, 最后一块插在i处, 那么你有pl的几率把左边推到, pr的几率把右边推到. 但是在期望里边, 你如果有(1 - pl - pr)的几率不倒的话, 那么你期望摆 1/(1 - pl - pr)次就能成功不倒. 那么我们就可以计算每一次的期望, 乘上这个次数即可.

设放好i左边的骨牌期望次数为E1, 放好i右边期望为E2, 放完的期望是E. 那么首先, 你要先把左边和右边摆好, 那么就是 E = E1 + E2.

插入第i块, 你有pl可能向左边倒, 那就得花费E1将左边摆好, 有pr可能向右边倒, 要E2将右边摆好. 那么每一次的期望就是: (1 + pl * E1 + pr * E2). 1就是你放的第i块. 你一共期望放 1 / (1 - pl - pr)次, 那么就是 (1 + pl * E1 + pr * E2) / (1 - pl - pr).

合起来就是:

E = E1 + E2 + (1 + pl * E1 + pr * E2) /( 1 - pl - pr).

接下来枚举i, 用f[i]表示摆好i块骨牌的期望. 那么对于每个i的最佳摆放策略, 都是枚举j(0 <=j < i)取min得出来的. 那么f[n]就是答案, 复杂度O(n^2). 考虑优化, 由于我们看到在从小到大枚举j的时候, E1不断变大(左边骨牌数变多), E2变小, 其他的都是常数, 这说明了是求E是单峰的. 由于我们在外层从小到大枚举i, 枚举i和i+1时j相同的话, E1相同, E2的话i+1要比i的大, 其他的都是常数, 这说明了i+1与i整体相似, 且j相同时值更大, 那么f[i+1]的min值取的j一定比f[i]取min时的j要大. 那么j是单增的. 复杂度降到O(n).

#include<stdio.h>
int n;double pl, pr, f[1005];
inline double dp(int i, int j){
    return f[j] + f[i - 1 - j] + (1 + pl * f[j] + pr * (f[i - 1 - j]))/ (1 - pl - pr);
}
int main(){
    int i, j;
    while(scanf("%d", &n) && n){
        scanf("%lf%lf", &pl, &pr);
        for(i = 1, j = 0; i <= n; ++i){
            while(j < i - 1 && dp(i, j) > dp(i, j + 1)) ++j;
            f[i] = dp(i, j);
        }
        printf("%.2f\n", f[n]);
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值