P3089 [USACO13NOV]POGO的牛Pogo-Cow P3089 [USACO13NOV]POGO的牛Pogo-Cow Solution Code
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
", ans);
}
int main(){
init();
solve();
return 0;
}