UVA 10529

题目描述


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 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.

输出

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.
 
题意:你试图把一些多米诺骨牌排成直线,然后推倒它们。但是如果你在放骨牌的时候不小心把刚放的骨牌碰倒了,它就会把相临的一串骨牌全都碰倒,而你的工作也被部分的破坏了。 比如你已经把骨牌摆成了DD__DxDDD_D的形状,而想要在x这个位置再放一块骨牌。它可能会把左边的一块骨牌或右边的三块骨牌碰倒,而你将不得不重新摆放这些骨牌。 这种失误是无法避免的,但是你可以应用一种特殊的放骨牌方法来使骨牌更多的向一个方向倒下。 给出你要摆放的骨牌数目,以及放骨牌时它向左和向右倒的概率,计算你为完成任务摆放的骨牌数目的平均数。假设你使用了最佳的摆放策略。 输入将包含至多100个测试点,每个测试点占一行,包含需要摆放的骨牌数目n (1≤n≤1000),以及两个非负实数Pl, Pr,表示骨牌向左和向右倒的概率。保证1<Pl+Pr≤0.5。 最后一个测试点包含一个数0。对于每个测试点输出题目要求的数目,保留两位小数。
 
思路:我们想成功的放置1块骨牌所需要的次数的期望是多少呢? 1/(1-pl-pr) 也就是说我们平均每放1/(1-pl-pr)个能成功一个
dp[i]表示成功放置长度为i的骨牌所需要次数的期望
用dp的思想我们之前已经得到dp[1]~dp[n-1]
我们假设长度为n的骨牌最后一块放置在i位置上
那么dp[n]=min(E[i]+dp[i-1]+dp[n-i]) 1<=i<=n就是枚举最后放的i位置 其中E[i]表示第i块能放成功的期望
现在我们处理E[i],也就是说在i位置放置成功我们平均需要几次呢?
1.直接放成功 这样我只要成功1次
2.放下之后向左倒了,首先发生这件事的概率是pl,之前成功放置i-1块的期望是dp[i-1]次,我们把dp[i-1]毁掉的概率是pl,对于向左倒,我们毁掉的块数的期望是dp[i-1]*pl
3.放下之后向右边倒,同理我们这时毁掉的块数的期望是pr*dp[n-i]
我们在i位置放置成功所需次数的期望是1+pl*dp[i-1]+pr*dp[n-i]
那么成功这么多次,我们又需要放几次呢? 注意到我们平均每放1/(1-pl-pr)个能成功一个
那么显然是(1+pl*dp[i-1]+pr*dp[n-i])/(1-pl-pr)次啊
这个地方比较绕...我们可以反向考虑
假如dp[i-1]=10,pl=0.4,那么我们放置i的时候如果向左倒,我们平均的就要多放置4个
dp[n-i]=20,pr=0.1那么我们放置i的时候如果向右倒,我们平均的就要多放置2个
也就是说我们先成功放置4个这样他就不会向左倒,再成功放置2个他就不会向右倒,我们此时再放置一个他就一定不会倒
这样我们需要成功放置4+2+1=7次,每次成功都需要1/(1-pl-pr)次放置
那么期望E[i]显然是(1+pl*dp[i-1]+pr*dp[n-i])/(1-pl-pr)
然后就n^2出答案了,网上有优化成O(n)的...
 1 #include <bits/stdc++.h>
 2 
 3 using namespace std;
 4 const int maxn = 1100;
 5 int n;
 6 double l,r;
 7 double dp[maxn];
 8 double calc (int n,int x)
 9 {
10     return (1.0+l*dp[x-1]+r*dp[n-x])/(1-l-r)+dp[x-1]+dp[n-x];
11 }
12 int main()
13 {
14     //freopen("de.txt","r",stdin);
15     while (~scanf("%d",&n)){
16         if (n==0) break;
17         scanf("%lf%lf",&l,&r);
18         dp[0]=0;
19         dp[1]=1.0/(1-r-l);
20         for (int i=2;i<=n;++i){
21             dp[i]=calc(i,i);
22             for (int j=1;j<=i;++j)
23                 dp[i]=min(dp[i],calc(i,j));
24         }
25         printf("%.2f
",dp[n]);
26     }
27     return 0;
28 }

优化成O(n)

 1 #include<iostream>  
 2 #include<cstdio>  
 3 using namespace std;  
 4 double f[100005];  
 5 double pl,pr,ans;  
 6 int n;  
 7 inline double cal(int n,int i){  
 8     return (pl*f[i-1]+pr*f[n-i]+1)/(1.0-pl-pr)+f[i-1]+f[n-i];//计算期望   
 9 }  
10 int main(){  
11     cin>>n;  
12     int i,j,x=1;  
13     cin>>pl>>pr;  
14     f[0]=0;f[1]=1.0/(1.0-pl-pr);//初始化,0张牌期望为0,一张牌期望为既不左倒也不右倒的概率   
15     for(i=2;i<=n;i++){  
16         f[i]=cal(i,x);  
17         while(x<i&&cal(i,x+1)<f[i]){f[i]=cal(i,x+1);x++;}//更优的决策点一定在上一次决策点之后   
18     }  
19     printf("%.2lf",f[n]);//保留两位   
20     return 0;  
21 }   
 
原文地址:https://www.cnblogs.com/agenthtb/p/7487788.html