定义 f [ i ] [ j ] f[i][j] f[i][j]表示区间 [ i , j ] [i,j] [i,j]中的牌都摆放完成的最小期望次数
每次枚举一个端点 k k k放在这里
设往左边倒的概率是 p 1 p_1 p1,往右边倒的概率是 p 2 p_2 p2,不倒的概率是 p 3 p_3 p3
f [ i ] [ j ] = p 1 ∗ ( 2 ∗ f [ i ] [ k − 1 ] + f [ k + 1 ] [ j ] + 1 ) + p 2 ∗ ( 2 ∗ f [ k + 1 ] [ j ] + f [ i ] [ k − 1 ] + 1 ) + p 3 ∗ ( f [ i ] [ k − 1 ] + f [ k + 1 ] [ j ] + 1 ) f[i][j]=p_1*(2*f[i][k-1]+f[k+1][j]+1)+p_2*(2*f[k+1][j]+f[i][k-1]+1)+p_3*(f[i][k-1]+f[k+1][j]+1) f[i][j]=p1∗(2∗f[i][k−1]+f[k+1][j]+1)+p2∗(2∗f[k+1][j]+f[i][k−1]+1)+p3∗(f[i][k−1]+f[k+1][j]+1)
这么转移直接是 O ( n 3 ) O(n^3) O(n3)爆炸
考虑每块骨牌都是一样的,直接定义 f [ i ] f[i] f[i]表示连续放置 i i i块相邻不倒的期望
那么考虑在位置 j j j放上一个骨牌
这个骨牌不倒的期望次数是 1 1 − p 1 − p 2 \frac{1}{1-p_1-p_2} 1−p1−p21
那么前面倒的期望是 1 1 − p 1 − p 2 − 1 = p 1 + p 2 1 − p 1 − p 2 \frac{1}{1-p_1-p_2}-1=\frac{p_1+p_2}{1-p_1-p_2} 1−p1−p21−1=1−p1−p2p1+p2
在倒的过程中,向左倒的概率是 p 1 p 1 + p 2 ∗ p 1 + p 2 1 − p 1 − p 2 = p 1 1 − p 1 − p 2 \frac{p_1}{p_1+p_2}*\frac{p_1+p_2}{1-p_1-p_2}=\frac{p_1}{1-p_1-p_2} p1+p2p1∗1−p1−p2p1+p2=1−p1−p2p1
向右倒的期望次数是 p 2 1 − p 1 − p 2 \frac{p_2}{1-p_1-p_2} 1−p1−p2p2
于是 f [ i ] = f [ j − 1 ] ∗ 1 − p 2 1 − p 1 − p 2 + f [ i − j ] ∗ 1 − p 1 1 − p 1 − p 2 + 1 1 − p 1 − p 2 f[i]=f[j-1]*\frac{1-p_2}{1-p_1-p_2}+f[i-j]*\frac{1-p_1}{1-p_1-p_2}+\frac{1}{1-p_1-p_2} f[i]=f[j−1]∗1−p1−p21−p2+f[i−j]∗1−p1−p21−p1+1−p1−p21
O ( n 2 ) O(n^2) O(n2)递推即可
#include <iostream>
#include <cstring>
#include <cmath>
using namespace std;
const int maxn = 1009;
double p1,p2,p3,f[maxn];
const int inf = 1e9;
int n;
int main()
{
while( cin >> n&&n )
{
for(int i=1;i<=n;i++) f[i] = inf;
cin >> p1 >> p2;
double p3 = 1.0-p1-p2;
f[1] = 1.0/p3;
for(int i=2;i<=n;i++)
for(int j=1;j<=i;j++)
f[i] = min( f[i],f[j-1]*(1-p2)/p3+f[i-j]*(1-p1)/p3+1.0/p3 );
printf("%.2lf\n",f[n] );
}
}