Codeforces 598D Igor In the Museum(概率DP)

@(K ACMer)

题意:
在x轴上有n颗高度为h的树,现在你将有 12 的概率,砍最左边的树,同样的概率砍最右边的树.然后对于每个树它有 p 的概率会向左边倒, (1p) 的概率会向右边倒.值得注意的是树的倒会引起类似多米诺骨牌效应,也就是如果一个树和它相邻的树和它的距离小于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;
}

你可能感兴趣的:(dp,codeforces)