@(K ACMer)
题意:
在x轴上有n颗高度为h的树,现在你将有 12 的概率,砍最左边的树,同样的概率砍最右边的树.然后对于每个树它有 p 的概率会向左边倒,(1−p) 的概率会向右边倒.值得注意的是树的倒会引起类似多米诺骨牌效应,也就是如果一个树和它相邻的树和它的距离小于h,它往这颗树倒的时候,该树也会向同样的方向倒.为你这些树全部倒下的期望覆盖地面长度是多少?
分析:
开始很很容易想到一个记忆化搜索的思路,定义 dfs(l,r,lf,rf) 为当前的最左边界的下标为 l 最右边界下标为r ,最靠近 l 的覆盖到了lf ,最靠近 r 的覆盖到了rf .这样再分为:1.左边的树向左边倒,2.左边的树向右边倒,3.右边的树向左边倒,4.右边的树向右边倒四种情况来转移就可.
但是写出来记忆优化的时候发现 lf 和 rf 都太大了,必须需要用map来离散化,所以记忆化的数组是 map<pair<int,int>,double>dp[l][r] 这样凭空多了个 O(logn) 的复杂度,既超时间也超内存.
最后发现其实没有必要记录 lf ,和 rf 这样大的数只需要记录当前 l 左边的树是向左倒还是向右倒,当前r 右边的树是向左倒还是向右倒,就可以推算出来 lf 和 rf .这是有状态 dp[l][r][2][2]
code:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <set>
#include <map>
#include <stack>
#include <vector>
#include <string>
#include <queue>
#include <cstdlib>
#include <cmath>
#include <algorithm>
using namespace std;
typedef pair<int, int> pii;
typedef long long ull;
typedef long long ll;
typedef vector<int> vi;
#define xx first
#define yy second
#define rep(i, a, n) for (int i = a; i < n; i++)
#define sa(n) scanf("%d", &(n))
#define vep(c) for(decltype((c).begin()) it = (c).begin(); it != (c).end(); it++)
const int mod = int(1e4) + 7, INF = 0x3fffffff, maxn = 1e5 + 12;
int n, h, v[2009];
double p, eps = 1e-11;
double dp[2009][2009][2][2];
double dfs(int l, int r, int lf, int rf)
{
if (l > r) return 0;
int li = l, ri =r;
if (dp[li][ri][lf][rf]) return dp[li][ri][lf][rf];
int lff = l > 0 ? (lf == 0 ? v[l - 1] : v[l - 1] + h ) : -INF;
int rff = r < n - 1 ? (rf == 1 ? v[r + 1] : v[r + 1] - h) : INF;
double ret = 0.0;
ret += p * 0.5 * (min(h, v[l] - lff) + dfs(l + 1, r, 0, rf));
int x = l + 1;
while (x <= r && v[x] < v[x - 1] + h) x++;
x--;
ret += (1 - p) * 0.5 * (min(v[x] - v[l] + h, rff - v[l]) + dfs(x + 1, r, 1, rf));
ret += (1 - p) * 0.5 * (min(h, rff - v[r]) + dfs(l, r - 1, lf, 1));
x = r - 1;
while (x >= l && v[x + 1] < v[x] + h) x--;
x++;
ret += p * 0.5 * (min(v[r] - v[x] + h, v[r] - lff) + dfs(l, x - 1, lf, 0));
return dp[li][ri][lf][rf] = ret + eps;
//return dp[li][ri][make_pair(lf, rf)] = ret + eps;
}
int main(void)
{
sa(n), sa(h);
cin >> p;
rep (i, 0, n) sa(v[i]);
sort(v, v + n);
printf("%.15f\n", dfs(0, n - 1, 0, 0));
return 0;
}