P3089 [USACO13NOV]POGO的牛Pogo-Cow
FJ给奶牛贝西的脚安装上了弹簧,使它可以在农场里快速地跳跃,但是它还没有学会如何降低速度。
FJ觉得让贝西在一条直线的一维线路上进行练习,他在不同的目标点放置了N (1 <= N <= 1000)个目标点,目标点i在目标点x(i),该点得分为p(i)。贝西开始时可以选择站在一个目标点上,只允许朝一个方向跳跃,从一目标点跳到另外一个目标点,每次跳跃的距离至少和上一次跳跃的距离相等,并且必须跳到一个目标点。
每跳到一个目标点,贝西可以拿到该点的得分,请计算他的最大可能得分。
输入输出格式
输入格式:
* Line 1: The integer N.
* Lines 2..1+N: Line i+1 contains x(i) and p(i), each an integer in the range 0..1,000,000.
输出格式:
* Line 1: The maximum number of points Bessie can receive.
输入输出样例
说明
There are 6 targets. The first is at position x=5 and is worth 6 points, and so on.
Bessie hops from position x=4 (8 points) to position x=5 (6 points) to position x=7 (6 points) to position x=10 (5 points).
题意:
给你n个点,先让你从这n个点中选择一个为起点,然后可以以这个点为起点然后选择一个方向(之后不能改变方向)开始蹦到下一个点,必须保证这一次蹦跳的距离要大于等于上一次的距离,把所有蹦跳经过的点的值加在一起就可以了
题解:
错误解析:
我先跑的以x增大为正方向
我原本想的是用一维数组dp[i],来存前i个点的蹦跳且最后一个到达的点是第i个点所获得的最大值,但是题上说了要保证这一次蹦跳的距离要大于等于上一次的距离,所以我就用了一个数组来保存这个dp[i]是从那个点蹦过来的,但是我还没有考虑周全,看一个例子:
给出的全是点的值,他们之间距离都是相差1
原始值 //1 7 5 10 15
dp //1 8 6 16
// 18
可见到dp跑到10这个位置的时候会出现两个值,一个16,蹦跳距离1;另一个18,蹦跳距离2;按我的代码我会选择18,那个存上一次蹦跳位置的数组会存放第二个点
这个时候dp到15的时候就会出错,因为从dp[4]没法向dp[5]转移,因为上一次蹦跳距离是2
这个时候结果就会出错<_>
正解:
dp[i][j]表示最后的那一次蹦跳是从j到i,所以我们再求dp[i][j]的最优解的时候还要枚举以j为终点的最后一蹦的另一个点是谁,三重for循环,感觉就凉凉了,所以肯定要降低复杂度
我们可以把i,和j枚举的顺序改变一下,这样可以在dp过程中就是在二维数组中一列一列地进行。这样有什么好处呢?
一列一列地进行就会有i的值一直在变大,但是j不变,意思就是蹦跳距离在越来越大(蹦跳远点不变,蹦跳终点越来越远)
这样的话我们枚举谁蹦到j是最优的时候,我们就可以倒着枚举,这样另一个点蹦跳到j的距离会越来越远,我们可以让一旦这个距离大于我们要求的
dp[i][j]的从j——>i我们就让这个循环停止,等到下一次i变大的时候,我们再让这个k接着上一次的往更大的间距枚举
状态转移方程:
sum=max(dp[j][k]+m[j].p,sum);
dp[i][j]=max(dp[i][j],sum);
代码:
1 #include <cmath> 2 #include <cstdio> 3 #include <cstring> 4 #include <iostream> 5 #include <algorithm> 6 using namespace std; 7 int n,ans; 8 int f[1001][1001]; 9 struct node { 10 int x,v; 11 bool operator <(const node &a)const { 12 return x<a.x; //重载运算符 13 } 14 } a[1001]; 15 int main() { 16 scanf("%d",&n); 17 for (int i=1; i<=n; i++)scanf("%d%d",&a[i].x,&a[i].v); 18 sort(a+1,a+n+1); 19 for (int j=1; j<=n; j++) { 20 int k=j-1,val=a[j].v; 21 for (int i=j+1; i<=n; i++) { 22 while ((k)and(a[i].x-a[j].x>=a[j].x-a[k].x)) 23 val=max(val,f[j][k]+a[j].v),k--; 24 f[i][j]=max(f[i][j],val); 25 ans=max(ans,val+a[i].v);} 26 //倒过来再求一次 27 }for (int j=n; j; j--) { 28 int k=j+1,val=a[j].v; 29 for (int i=j-1; i; i--) { 30 while ((k<=n)and(a[k].x-a[j].x<=a[j].x-a[i].x)) 31 val=max(val,f[j][k]+a[j].v),k++; 32 f[i][j]=max(f[i][j],val); 33 ans=max(ans,val+a[i].v);} 34 }printf("%d\n",ans); 35 return 0; 36 }