POJ 2327 Dumb Bones

Description

You are trying to set up a straight line of dominos, standing on end, to be pushed over later for your entertainment. (Sure, it seems pointless to set something up only to knock it down again, but you have some strange hobbies) The tricky thing about setting dominos, however, is that if you make a mistake and knock one over as you place it, it will knock down any adjacent line of consecutive dominos on one side of it, partially ruining your work.
For instance, if you've already placed dominos in the pattern DD__DxDDD_D, and you try placing a domino at position x, there is a chance it will fall and knock over the domino to the left or the three dominos to its right, forcing you to place them again.

This human error is somewhat unavoidable, but you can make the odds somewhat more favourable by using a domino-placing technique that leads to dominos falling in one direction more often than in the other.

Given the number of dominos you are trying to set up, and the probability that you'll knock over any individual domino either to the left or to the right while placing it, determine the average number of dominos you'll need to place before you finish. Assume that you're using an optimal placement strategy.

Input

Input will consist of up to 100 cases. Each case consists of one line of input. It will contain the number of dominos to place, n, 1 <= n <= 1000, followed by nonnegative values Pl and Pr, indicating the probability of any domino falling to the left or to the right when placed. You may assume 0 < Pl + Pr <= 0.5.

The last test case is followed by a line containing a single 0.

Output

For each case, output the expected number of dominos that will need to be placed before you finish, accurate to two digits after the decimal.

Sample Input

10 0.25 0.25
10 0.1 0.4
10 0.0 0.5
0

Sample Output

46.25
37.28
20.00

Source


解题思路: 概率DP,首先设dp[i]表示长度为i的多米诺骨牌摆放次数的期望,然后我们枚举最后一个牌的放置位置,这个牌有pl的概率向左,pr的概率向右,1-pl-pr的概率不偏。
则我们摆放最后一颗牌时最少需要1/(1-pl-pr)次才能放正。加入向左偏,则左边的牌需要重新摆放,向右偏,右边的牌需要重新摆放,因此我们不难得出状态转移方程为:
dp[i] = (dp[l]+1)*pl/(1-pl-pr) + (dp[r]+1)*pr/(1-pl-pr) +(1-pl-pr)*(dp[l]+dp[r]+1)/(1-pl-pr),化简后最终得dp[i]=(1-pr)*dp[l]/(1-pl-pr)+(1-pl)*dp[r]/(1-pl-pr)+1/(1-pl-pr)。很显然复杂度为O(n^2)。
优化方案:

可以根据动态规划时候,dp[i]这个数组在找寻最小值的时候,其实方程是满足一个下凹函数的,所以这步实际上可以利用三分求解,复杂度为O(nlog(n)),然后实际上,对于下凹函数,那么其实对于下次找最小值的位置,是不会减小的,因此如果每次维护记录下上次找到答案的位置,这样均摊下来,时间复杂度就能优化到O(n)

#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
using namespace std;
const int maxn = 1010;
double dp[maxn];
int n;
double pl, pr;

double cal(int l, int r) {
    return  (1-pr)/(1-pl-pr)*dp[l] + (1-pl)/(1-pr-pl)*dp[r] + 1/(1-pl-pr);
}

int main() {

    //freopen("aa.in", "r", stdin);

    while(scanf("%d", &n) && n) {
        scanf("%lf %lf", &pl, &pr);
        dp[0] = 0;
        dp[1] = 1.0 / (1 - pl - pr);
        /*
        for(int i = 2; i <= n; ++i) {
            dp[i] = cal(0, i - 1);
            for(int j = 1; j < i; ++j) {
                dp[i] = min(dp[i], cal(j, i - j - 1));
            }
        }
        */
        // 优化后的状态转移方程

        int lp = 0;  // 记录上次找到最优值的位置
        for(int i = 2; i <= n; ++i) {
            dp[i] = cal(lp, i - lp - 1);
            for(int j = lp + 1; j < i; ++j) {
                double t = cal(j, i - j - 1);
                if(dp[i] >= t) {
                    dp[i] = t;
                    lp = j;
                } else {
                    break;
                }
            }
        }

        printf("%.2lf\n", dp[n]);
    }
    return 0;
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值