USACO13NOV Pogo-Cow S

题目链接

Solution

刚开始没看到向任意方向跳,惨遭 WA 54pts QWQ

\(f_{i,j}\) 表示当前是 \(i\) 号节点,上一个从 \(j\) 处跳过来的最大价值。那么得到转移方程式:

\[f_{i,j}=\max(f_{i,j},f_{j,k}+y_i) (x_j-x_k≤x_i-x_j) \]

如果暴力枚举 \(i,j,k\),复杂度 \(O(n^3)\),很明显不能通过此题。考虑优化。设:

\[Max_{i,j}=\max(f_{i,k}) (1≤k<j) \]

相当于一个前缀最大值。那么这个数组怎么用呢?

如果对 \(x_i\) 进行排序,那么对于 \(f_{i,j}\),如果它能从 \(f_{j,k}\) 转移过来,那它也能从 \(f_{j,h} (h<k)\) 转移过来。这时候我们只需把转移方程式变成:

\[f_{i,j}=\max(f_{i,j},Max_{j,k}) \]

其中 \(k\)距离 i 最近的可以到达的点。现在的问题变成了寻找 \(k\),在 \([0,j-1]\) 范围内二分即可。

注意: 还是开头那个问题,需要分别按照 \(x_i\) 从小到大和从大到小排序,dp 两遍才能得到答案。

时间复杂度 \(O(n^2\log_n)\)

Code

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#define LL long long
using namespace std;

const int N = 2333;
struct node { LL x, y; } a[N];
LL n, ans = 0, f[N][N], Max[N][N];

LL GetPos(LL x, LL y)
{
    LL l = 0, r = x - 1;
    while(l + 1 < r)
    {
        LL mid = (l + r) >> 1;
        if(abs(a[x].x - a[mid].x) >= abs(a[y].x - a[x].x)) l = mid;
        else r = mid;
    }
    if(abs(a[x].x - a[r].x) >= abs(a[y].x - a[x].x)) return r;
    return l;
}

bool cmp1(node a, node b) { return a.x < b.x; }
bool cmp2(node a, node b) { return a.x > b.x; }

void dp()
{
    memset(f, -0x7f7f7f, sizeof(f));
    a[0].x = -1e18, a[0].y = 0;
    f[0][0] = Max[0][0] = 0;
    for(int i = 1; i <= n; i++)
    {
        for(int j = 0; j < i; j++)
            f[i][j] = max(f[i][j], Max[j][GetPos(j, i)] + a[i].y);
        for(int j = 0; j < i; j++)
            Max[i][j] = max(Max[i][j - 1], f[i][j]);
    }
    for(int i = 1; i <= n; i++)
        for(int j = 0; j < i; j++)
            ans = max(ans, f[i][j]);
    return ;
}

int main()
{
    scanf("%lld", &n);
    memset(f, -0x7f7f7f, sizeof(f));
    memset(Max, -0x7f7f7f, sizeof(Max));
    for(int i = 1; i <= n; i++) scanf("%lld%lld", &a[i].x, &a[i].y);
    sort(a + 1, a + n + 1, cmp1), dp();
    sort(a + 1, a + n + 1, cmp2), dp();
    printf("%lld", ans);
    return 0;
}
posted @ 2020-10-14 11:38  Nyxia  阅读(76)  评论(0编辑  收藏  举报