Codeforces Round #331 (Div. 2) .D - Wilbur and Trees, 枚举情况的DFS

题解出处:http://www.cnblogs.com/qscqesze/p/4971459.html
膜一发卿学姐Orz……………..

题意是给你n棵树,每棵树的高度都一样的为h,以及p,砍树的时候会往左边倒的概率,(1-p就是往右边倒的概率),然后第二行会给你没课树的坐标。
砍树的人每次都会有0.5的概率砍最左边的树,0.5的概率砍最右边的树,砍完一棵树后这棵树向左或右倒下,如果他在倒下的时候砸到了另外一棵树,那么那棵树也会以相同的方向倒下, 问砍完所有树后,覆盖地面长度的数学期望。

思路: 当时我们看了这道题就直接打算放弃了,我们都觉得是一道dp,而且我个人 还对 数学期望有点犯迷糊(高中知识都还给老师了,就是所有情况的 概率* 长度 的和)。所以蒟蒻的我们并不能做出来。 后来看了下题解,发现真是个不难的题目(当然,自己脑残)
这题并不需要用dp,因为对于给出的每一种情况,我们要考虑的就是以下几种条件:
区间 为 l–r:l是最左边的树,r是最右边的树
1.砍最左边的树
(1)。若是砍左边的树 且 左边的树向左倒,需要考虑l-1 是否往右倒下,距离是 min ( h , v[l] - v[ l-1] - f1(是否向右)*h ), (这个地方还是和数学有关的)所以这里的f1 若为0 则代表l-1向左到了,若是为1,明显代表l-1向右倒下。明显还没有做完,自然要做下一个区间(l+1,r,0,f2)因为对于下一个区间l相当于l-1,向左倒即f1为0. 同理我们设f2为判断是否r+1向左或右倒。

(2)。若是砍左边的树 且 左边的树向右倒,那么就需要判断一下,这棵树能推倒多少棵树, 假如能向右推倒到L,则 这一段距离就是(v[ L ]-v[ l ]+h)因为肯定推不倒第L+1棵(如果存在的话)
在思考一下,若是L < r,则这一段距离是没有问题的,但我们肯定还要对区间(L+1, r ,1,f2)进行处理,
但若是L>=r,距离就会发生改变,因为我们还要考虑r+1是否向左倒下,距离应该是:( v[ r ] - v[ l ] + min( h , v[ r + 1 ] - v [ r ]-f2*h) ),这个地方写错过一次: ( v[ r] - v[ l ] + h f2 * h),但其实 h很有可能大于v[r+1]-v[r],所以自然不能够xjb写!

2..砍最右边的树
同理也有两种情况,分别是向右倒,和向左倒
同理:向右倒自然比较简单,即 min( h , v[r+1] - v[ r ]- f2*h ) +dfs(l,r-1,f1 , 0) ;
向左倒的话,自然需要判断是否能推到l,如果能则 为(v[r]-v[l] + min( h, v[l]-v[l-1] -f1*h ) );
若不能 设R 为能推倒的最左边的树 则为 v[r] - v[ R] +h + dfs (l, R,f1,1) ; 这里就不用取min,因为砸不到R-1,所以 h肯定

#include<iostream>
#include<stdio.h>
#include<algorithm>
using namespace std;
#define maxn 2005
const int inf = 1e9;
double dp[maxn][maxn][2][2];
int vis[maxn][maxn][2][2];
int n;
double h,p;
int v[maxn];
int dl[maxn],dr[maxn];
double dfs(int l,int r,int f1,int f2) // l,r->1-n  f1:l-1这棵树是否倒向右,f2:r+1这棵树是否倒向了左边
{
    //cout<<l<<" "<<r<<" "<<f1<<" "<<f2<<endl;
    if(vis[l][r][f1][f2])
        return dp[l][r][f1][f2];
    if(l>r)
        return 0;
    vis[l][r][f1][f2]=1; //
    // 一共就几种情况

    double ans = dp[l][r][f1][f2];
    ans+=p*0.5* (   min(h*1.00,v[l]-v[l-1]-f1*h) + dfs(l+1,r,0,f2) );
    //第l棵树倒向左边的数学期望,f1=1则代表第l-1棵树倒向右边,然后递归至下一层
    ans+=(1-p)*0.5*(min(h*1.0,v[r+1]-v[r]-f2*h)+dfs(l,r-1,f1,0));
    //第r棵树倒向右边,f2=1表示第r+1棵树倒向左边,

    int L = dr[l];//第l棵树向右边倒,能够推到哪棵树
    int R = dl[r];//第r棵树向左边倒,能够推到哪棵树

    if(R<=l) //若右边树r能够倒下到 区间最左(l),那么砍最后边的树向左边直接到l。
        ans+=p*0.5*( v[r]-v[l] +  min(h,v[l]-v[l-1]-f1*h) );//不要忽视l-1是否倒向右
    else    //否则不会倒到l,还需要递归至下一层。
        ans+=p*0.5*( v[r]-v[R]+h+dfs(l,R-1,f1,1) ); //推到r能倒到的极限位置R
    if(L>=r) //若第l能够推到r,同理
        ans+=(1-p)*0.5*(v[r]-v[l]+min(h,v[r+1]-v[r]-f2*h));
    else
        ans+=(1-p)*0.5*(v[L]-v[l]+h+dfs(L+1,r,1,f2));
    dp[l][r][f1][f2] = ans;
    return ans;
}
int main()
{
    scanf("%d%lf",&n,&h);
    scanf("%lf",&p);
    for(int i=1;i<=n;i++)
        scanf("%d",&v[i]);
    sort(v+1,v+1+n);
    v[n+1]=inf;
    v[0]=-inf;
    dr[n]=n;dl[1]=1;     //dl 代表i往左边倒最远 会压到哪一棵树,同理dr表示往右倒最远会压到哪一颗
    for(int i=n-1;i>=1;i--){
        if(v[i+1]-v[i]<h)
            dr[i]=dr[i+1];
        else 
            dr[i]=i;
    }
    for(int i=2;i<=n;i++){
        if(v[i]-v[i-1]<h)
            dl[i]=dl[i-1];
        else
            dl[i]=i;
    }
    printf("%.15f\n",dfs(1,n,0,0));
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值