[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,i−1]&[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;
}