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.

 

输入输出样例

输入样例#1: 复制
6 
5 6 
1 1 
10 5 
7 6 
4 8 
8 10 
输出样例#1: 复制
25 

说明

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 }
View Code

 

posted @ 2019-06-27 17:34  kongbursi  阅读(242)  评论(0编辑  收藏  举报