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;
	}