UVA10529 Dumb Bones

传送门

翻译有些不清楚,意思就是骨牌不一定要按从左到右的顺序放,可以左边放一个,右边放一个,再中间放一个

然后每个骨牌都可能往左或往右倒,一旦倒了,倒的一边的所有骨牌都要重新放

然后问你,最小期望放置次数是多少

考虑每个骨牌的影响,设$f [ i ]$ 表示放$ i $个骨牌的的最小期望放置次数

那么显然 $f [ 1 ] = 1/(1-pl-pr)$

枚举此次放置的位置$ j $,设左边有$ L $个骨牌,右边有 $R$ 个骨牌

那么期望花费就是左边的期望花费$f[L]$加右边的期望花费$f[R]$

加上放最后一块的期望$\frac{(1+P_l\cdot f[L]+P_r\cdot f[R])}{(1-P_l-P_r)}$

$P_l\cdot f[L]$ 是骨牌往左倒的期望花费,$P_r\cdot f[R]$同理,注意还要保证最后放置时不倒,所以期望要再除一个成功概率

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
using namespace std;
typedef long long ll;
inline ll read()
{
    ll x=0,f=1; char ch=getchar();
    while(ch<'0'||ch>'9') { if(ch=='-') f=-1; ch=getchar(); }
    while(ch>='0'&&ch<='9') { x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); }
    return x*f;
}
const int N=1e3+7;
const double INF=1e9+7;
int n;
double f[N],pl,pr,t;//t是放置一个骨牌的成功概率
inline double slove(int l,int r)
{
    return f[l]+f[r]+(1+pl*f[l]+pr*f[r])/t;
}
int main()
{
    n=read();
    while(n)
    {
        scanf("%lf%lf",&pl,&pr);
        t=1.0-pl-pr;
        f[0]=0; f[1]=1/t;
        for(int i=2;i<=n;i++)
        {
            f[i]=slove(0,i-1);
            for(int j=1;j<i;j++)
                f[i]=min(f[i],slove(j,i-j-1));
        }
        printf("%.2lf\n",f[n]);
        n=read();
    }
    return 0;
}

可以发现随着 j 的变化,期望是一个下凹函数

随着 i 的增长,下凹的位置是保持不降的,所以我们可以得出一个均摊O(n)的算法

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
using namespace std;
typedef long long ll;
inline ll read()
{
    ll x=0,f=1; char ch=getchar();
    while(ch<'0'||ch>'9') { if(ch=='-') f=-1; ch=getchar(); }
    while(ch>='0'&&ch<='9') { x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); }
    return x*f;
}
const int N=1e3+7;
const double INF=1e9+7;
int n;
double f[N],pl,pr,t;
inline double slove(int l,int r)
{
    return f[l]+f[r]+(1+pl*f[l]+pr*f[r])/t;
}
int main()
{
    n=read();
    while(n)
    {
        scanf("%lf%lf",&pl,&pr);
        t=1.0-pl-pr;
        f[0]=0; f[1]=1/t;
        int pos=0;//pos记录上一次下凹的位置
        for(int i=2;i<=n;i++)
        {
            f[i]=slove(pos,i-pos-1);
            for(int j=pos+1;j<i;j++)//下凹位置保持不降
            {
                double s=slove(j,i-j-1);
                if(s<=f[i]) { f[i]=s; pos=j; }
                else break;
            }
        }
        printf("%.2lf\n",f[n]);
        n=read();
    }
    return 0;
}

 

posted @ 2018-10-17 15:48  LLTYYC  阅读(251)  评论(0编辑  收藏  举报