「Codeforces」596D (dfs+区间dp+细节)

题意:原题在这

Wilbur the pig really wants to be a beaver, so he decided today to pretend he is a beaver and bite at trees to cut them down.

There are nn trees located at various positions on a line. Tree ii is located at position x_{i}xi . All the given positions of the trees are distinct.

The trees are equal, i.e. each tree has height hh . Due to the wind, when a tree is cut down, it either falls left with probability pp , or falls right with probability 1-p1p . If a tree hits another tree while falling, that tree will fall in the same direction as the tree that hit it. A tree can hit another tree only if the distance between them is strictly less than hh .

For example, imagine there are 44 trees located at positions 11 , 33 , 55and 88 , while h=3h=3 and the tree at position 11 falls right. It hits the tree at position 33 and it starts to fall too. In it's turn it hits the tree at position 55 and it also starts to fall. The distance between 88 and 55is exactly 33 , so the tree at position 88 will not fall.

As long as there are still trees standing, Wilbur will select either the leftmost standing tree with probability 0.50.5 or the rightmost standing tree with probability 0.50.5 . Selected tree is then cut down. If there is only one tree remaining, Wilbur always selects it. As the ground is covered with grass, Wilbur wants to know the expected total length of the ground covered with fallen trees after he cuts them all down because he is concerned about his grass-eating cow friends. Please help Wilbur.

给定树的数量,高度和位置,每棵树有p概率向左倒,1-p向右倒

小sb会选择这一排树的端点砍倒

求出倒下的树所覆盖的地面长度

 

做法:(详见行内注释)

1. 递归预处理每棵树向左/右倒压倒的最大长度

2. dp[l][r][0/1][0/1]表示从l到r区间l-1、r+1向左/右的期望

情况:
  (1).最左边向左倒和最右边向右倒,均不影响,取min计算重叠
  (2).计算向区间中央倒

 

代码:

#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
#define inf 999999999
#define maxn 100000005
using namespace std;

int n;
int pos[2005],maxl[2005],maxr[2005];
double p,h;
double dp[2005][2005][2][2];//dp[l][r][0/1][0/1]表示从l到r区间l-1、r+1向左/右的期望

void pre()//递归预处理每棵树向左/右倒压倒的最大长度
{
    maxl[1]=1;
    for(int i=2;i<=n;i++)
    {
        if(pos[i]-pos[i-1]<h) maxl[i]=maxl[i-1];
        else maxl[i]=i;
    }
    maxr[n]=n;
    for(int i=n-1;i>=1;i--)
    {
        if(pos[i+1]-pos[i]<h) maxr[i]=maxr[i+1];
        else maxr[i]=i;
    }
}
/*

*/
double dfs(int l,int r,int sl,int sr)
{
    //边界处理
    if(l>r) return 0;
    if(dp[l][r][sl][sr]!=0) return dp[l][r][sl][sr];//记忆化搜索,dp一算到底
    
    //计算端点
    double temp=dp[l][r][sl][sr];
    //最左边向左倒和最右边向右倒,均不影响,取min计算重叠
    temp+=0.5*p*(min(pos[l]-pos[l-1]-sl*h,h)+dfs(l+1,r,0,sr));
    temp+=0.5*(1-p)*(min(pos[r+1]-pos[r]-sr*h,h)+dfs(l,r-1,sl,0));
    //计算向区间中央倒
    if(maxr[l]>=r) temp+=0.5*(1-p)*(min(pos[r+1]-pos[r]-sr*h,h)+pos[r]-pos[l]);
    else temp+=0.5*(1-p)*(pos[maxr[l]]-pos[l]+h+dfs(maxr[l]+1,r,1,sr));

    if(maxl[r]<=l) temp+=0.5*p*(min(pos[l]-pos[l-1]-sl*h,h)+pos[r]-pos[l]);
    else temp+=0.5*p*(pos[r]-pos[maxl[r]]+h+dfs(l,maxl[r]-1,sl,1));

    return dp[l][r][sl][sr]=temp;
}

/*  9 2 0.800000
    3 5 7 9 11 13 15 17 18
    */
int main()
{
    cin>>n>>h>>p;
    for(int i=1;i<=n;i++) cin>>pos[i];
    sort(pos+1,pos+n+1);
    pos[0]=-inf;pos[n+1]=inf;
    pre();
    memset(dp,0,sizeof(dp));
    printf("%.20lf\n",dfs(1,n,0,0));
    return 0;
}

 

转载于:https://www.cnblogs.com/LocaEtric/p/9614945.html

区间DP是一种动态规划的方法,用于解决区间范围内的问题。在Codeforces竞赛中,区间DP经常被用于解决一些复杂的字符串或序列相关的问题。 在区间DP中,dp[i][j]表示第一个序列前i个元素和第二个序列前j个元素的最优解。具体的转移方程会根据具体的问题而变化,但是通常会涉及到比较两个序列的元素是否相等,然后根据不同的情况进行状态转移。 对于区间长度为1的情况,可以先进行初始化,然后再通过枚举区间长度和区间左端点,计算出dp[i][j]的值。 以下是一个示例代码,展示了如何使用区间DP来解决一个字符串匹配的问题: #include <cstdio> #include <cstring> #include <string> #include <iostream> #include <algorithm> using namespace std; const int maxn=510; const int inf=0x3f3f3f3f; int n,dp[maxn][maxn]; char s[maxn]; int main() { scanf("%d", &n); scanf("%s", s + 1); for(int i = 1; i <= n; i++) dp[i][i] = 1; for(int i = 1; i <= n; i++) { if(s[i] == s[i - 1]) dp[i][i - 1] = 1; else dp[i][i - 1] = 2; } for(int len = 3; len <= n; len++) { int r; for(int l = 1; l + len - 1 <= n; l++) { r = l + len - 1; dp[l][r] = inf; if(s[l] == s[r]) dp[l][r] = min(dp[l + 1][r], dp[l][r - 1]); else { for(int k = l; k <= r; k++) { dp[l][r] = min(dp[l][r], dp[l][k] + dp[k + 1][r]); } } } } printf("%d\n", dp[n]); return 0; } 希望这个例子能帮助你理解区间DP的基本思想和应用方法。如果你还有其他问题,请随时提问。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值