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; }