P3089 [USACO13NOV]POGO的牛Pogo-Cow

P3089 [USACO13NOV]POGO的牛Pogo-Cow

FJ给奶牛贝西的脚安装上了弹簧,使它可以在农场里快速地跳跃,但是它还没有学会如何降低速度。

FJ觉得让贝西在一条直线的一维线路上进行练习,他在不同的目标点放置了N (1 <= N <= 1000)个目标点,目标点i在目标点x(i),该点得分为p(i)。贝西开始时可以选择站在一个目标点上,只允许朝一个方向跳跃,从一目标点跳到另外一个目标点,每次跳跃的距离大于等于上一次跳跃的距离相等,并且必须跳到一个目标点。

每跳到一个目标点,贝西可以拿到该点的得分,请计算他的最大可能得分。

Solution

这题日了我好久啊
刚开始推了一下以为是单调队列
后面发现有个点(没打草稿脑算)推错了。。

\(dp[i][j]\) 为跳到第 \(i\) 个点, 从 \(j\) 点跳过来的最大价值
那么容易想到如下转移: \[dp[i][j] = \max_{dis(i, j) \geq dis(j, k)}dp[j][k] + v[i]\]
其复杂度为 \(O(n^{3})\), 难以接受
考虑优化, 能不能省掉某一维枚举
当然从可行性入手, 观察如下式子: \[dis(i, j) \geq dis(j, k)\]
对于一个特定的 \(j\) , 我们将其dp数组全部列出 \[dp[j][1], dp[j][2], dp[j][3],...,dp[j][j]\]
排除 \(i\) 的影响, 我们先把式子写成 \(dis(j, k) \leq t\) \[abs(p_{j} - p_{k}) \leq t\]
\(i, j\) 不变, 观察 \(k\) 对不等式成立的影响, 发现当 \(k\) 越小, 不等式越有可能成立
也就是说, 存在一个位置 \(x\) , 满足:\[abs(p_{j} - p_{x}) \leq t\] \[abs(p_{j} - p_{x - 1}) > t\]
\([x, j]\) 范围内 \(dp[j][k]\) 全部可取, \([1, x - 1]\) 范围内 \(dp[j][k]\) 全部不可取
对于特定的 \(j\)\(t\) 随着 \(i\) 的增大而增大, 所以 \(x\)\(i\) 的增大而减小
于是对于每个 \(j\) 维护一个位置 \(x\), 记为 \(head[j]\) 表示其右端全部可取
同时维护可取的部分的最大值即可

Code

#include<iostream>
#include<cstdio>
#include<queue>
#include<cstring>
#include<algorithm>
#include<climits>
#define LL long long
#define REP(i, x, y) for(int i = (x);i <= (y);i++)
using namespace std;
int RD(){
    int out = 0,flag = 1;char c = getchar();
    while(c < '0' || c >'9'){if(c == '-')flag = -1;c = getchar();}
    while(c >= '0' && c <= '9'){out = out * 10 + c - '0';c = getchar();}
    return flag * out;
    }
const int maxn = 2019;
int num, ans;
struct Point{int p, v;}p[maxn];
int dp[maxn][maxn];//跳到i,从j跳过来的最大得分
bool cmp1(Point a, Point b){return a.p < b.p;}
bool cmp2(Point a, Point b){return a.p > b.p;}
void init(){
    num = RD();
    REP(i, 1, num)p[i] = (Point){RD(), RD()};
    }
int head[maxn];//目前合法的第一个dp[i][x]位置
int maxx[maxn];//目前最大的关于i的上一项
void DP(){
    memset(dp, 0, sizeof(dp));
    REP(i, 1, num)dp[i][i] = maxx[i] = p[i].v, head[i] = i;//作为起点
    REP(i, 1, num){
        REP(j, 1, i - 1){
            dp[i][j] = maxx[j] + p[i].v;
            ans = max(ans, dp[i][j]);
            }
        REP(j, 1, i){
            while(head[j] > 1 && (abs(p[i + 1].p - p[j].p) >= abs(p[j].p - p[head[j] - 1].p))){
                head[j]--;
                maxx[j] = max(maxx[j], dp[j][head[j]]);
                }
            }
        }
    }
void solve(){
    sort(p + 1, p + 1 + num, cmp1);//顺着排序
    DP();
    sort(p + 1, p + 1 + num, cmp2);//倒着排序
    DP();
    printf("%d\n", ans);
    }
int main(){
    init();
    solve();
    return 0;
    }

猜你喜欢

转载自www.cnblogs.com/Tony-Double-Sky/p/9885469.html