[SDOI2008]Sue的小球(区间Dp)

在这里插入图片描述

[SDOI2008]Sue的小球

区间 D P DP DP

题目

这一道题的区间 D P DP DP还是比较显然:首先可以考虑开始的总的答案是所有的初速度的和,之后再在收集的过程中逐渐减少答案,最后得到的答案最大。同时又由于速度都是线性减少的,那么 d p [ i ] [ j ] dp[i][j] dp[i][j]很明显就应该表示从 i i i j j j这一段区间表示的答案(也就是答案减少的最小值)。这里,注意到转移方程中既可以从左边的点与右边的区间转移,又可以从右边的点与左边的区间转移,这样不妨设 d p [ i ] [ j ] dp[i][j] dp[i][j]表示区间为 [ i , j ] [i, j] [i,j]同时人在 j j j这一个位置的时候的答案减少最小值。


新的问题出来了,就是最重要的部分。

我们在转移的时候需要使用新的节点的速度 ∗ * 已经经过的时间,但是已经经过的时间显得非常难维护(非要使用 p a i r pair pair也可以),这里注意到所有的小球都是要接住的,那么对于某一段区间,这一段区间外面的小球也一定会下降这个区间答案所使用的时间,于是我们可以在统计区间 [ i , j ] [i, j] [i,j]的时候同时把位于区间 [ 1 , i − 1 ] & [ j + 1 , n ] [1, i - 1] \& [j + 1, n] [1,i1]&[j+1,n]的小球的答案减少也统计到这里面来。

离散化等简单的操作就不说了。

代码:

#include <bits/stdc++.h>
using namespace std;
const int N = 1000 + 5;
int n, x0;
struct Egg {
    int x, y, v;
    Egg (int a, int b, int c) {
        x = a;
        y = b;
        v = c;
    }
    Egg () {
    }
}info[N];
int num[N];
inline int Abs (int u) {
    return u < 0 ? -1 * u : u;
}
inline int Find (int u) {
    int l = 1, r = n, mid;
    while (l < r) {
        mid = (l + r) / 2;
        if (num[mid] < u) {
            l = mid + 1;
        }
        else {
            r = mid;
        }
    }
    return l;
}
int dp[N][N], sum[N], tot;
bool cmp (Egg m, Egg n) {
    return m.x < n.x;
}
int main () {
    #ifdef wll
    freopen ("testdata.in", "r", stdin);
    #endif
    memset (dp, 127, sizeof (dp));
    scanf ("%d%d", &n, &x0);
    for (int i = 1; i <= n; ++i) {
        scanf ("%d", &info[i].x);
        num[i] = info[i].x;
    }
    for (int i = 1; i <= n; ++i) {
        scanf ("%d", &info[i].y);
        tot += info[i].y;
    }
    for (int i = 1; i <= n; ++i) {
        scanf ("%d", &info[i].v);
    }
    sort (info + 1, info + n + 1, cmp);
    sort (num + 1, num + n + 1);
    for (int i = 1; i <= n; ++i) {
        sum[i] = sum[i - 1] + info[i].v;
    }
    for (int i = 1; i <= n; ++i) {
        info[i].x = Find (info[i].x);
        dp[i][i] = sum[n] * Abs (x0 - num[info[i].x]);
    }
    for (int k = 1; k <= n; ++k) {
        for (int i = 1; i <= n - k + 1; ++i) {
            int j = i + k - 1;
            dp[i][j] = min (dp[i][j], dp[i][j - 1] + (num[info[j].x] - num[info[j - 1].x]) * (sum[n] - sum[j - 1] + sum[i - 1]));
            dp[i][j] = min (dp[i][j], dp[j - 1][i] + (num[info[j].x] - num[info[i].x]) * (sum[n] - sum[j - 1] + sum[i - 1]));
            dp[j][i] = min (dp[j][i], dp[j][i + 1] + (num[info[i + 1].x] - num[info[i].x]) * (sum[n] - sum[j] + sum[i]));
            dp[j][i] = min (dp[j][i], dp[i + 1][j] + (num[info[j].x] - num[info[i].x]) * (sum[n] - sum[j] + sum[i]));
        }
    }
    printf ("%.3lf\n", 	(tot - min (dp[1][n], dp[n][1])) / 1000.0);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值