把博客园图标替换成自己的图标
把博客园图标替换成自己的图标end

弹簧高跷 [dp+改变枚举顺序小优化]

Description

在草场上有一条直线,直线上有若干个目标点。每个目标点都有一个分值和一个坐标。现在你可以选择其中任意一个目标点开始跳,只能沿一个方向跳,并且必须跳到另一个目标点。且每次跳的距离都不能少于上一次的距离。请问你能得到的最大分值是多少?

Input

第一行一个整数N(1<=N<=1000).接下来从第二行到第N+1行,每一行包含两个整数x(i)和p(i),每个整数在区间[0,1000000]之间。

Output

最大能获得的分值。

Sample Input

6
5 6
1 1
10 5
7 6
4 8
8 10

Sample Output

25

分析

很容易想到dp
可以比较轻松地写出状态转移方程:
定义dp[i][j]为现在在第i个目标点,从第j个目标点跳过来。

由于这道题可以向两个方向跳,所以我们需要做两次,分类讨论如下:

向右跳

在这种情况下,需要满足
x[i]-x[j]>=x[j]-x[k]

向左跳

同理,在这种情况下需要满足
x[j]-x[i]>=x[k]-x[j]

由于这道题没有规定起点 k可以等于j ans要在所有的dp中取一个最大值
同时,根据题意 所有的dp[i][i]要初始化为p[i]

Addition

可是这样的话,枚举i,j,k三重循环会超时
于是我们观察到,当j的位置确定时,i,k的范围也确定了,一个在左边,一个在右边,那么就可以先枚举j,再去枚举i和k,这样枚举i、k的时间合在一起其实就只有O(n)
就可以愉快地把复杂度降下来了。

代码

#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
#define MAXN 1005
int n,dp[MAXN][MAXN],ans;
struct node{
	int x,p;
}a[MAXN];
bool cmp(node r,node t)
{
	return r.x<t.x;
}
int main()
{
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
		scanf("%d %d",&a[i].x,&a[i].p);
	sort(a+1,a+n+1,cmp);
	for(int j=1;j<=n;j++)
	{
		dp[j][j]=a[j].p;
		ans=max(dp[j][j],ans);
		for(int i=j+1;i<=n;i++)
			for(int k=j;k>=1;k--)//从两边散开 
			{
				if(a[i].x-a[j].x<a[j].x-a[k].x) continue;
				dp[i][j]=max(dp[i][j],dp[j][k]+a[i].p);
				ans=max(ans,dp[i][j]);
			}
	}
	memset(dp,0,sizeof(dp));
	for(int j=n;j>=1;j--)
	{
		dp[j][j]=a[j].p;
		ans=max(dp[j][j],ans);
		for(int i=j-1;i>=1;i--)
			for(int k=j;k<=n;k++)
			{
				if(a[j].x-a[i].x<a[k].x-a[j].x) continue;
				dp[i][j]=max(dp[i][j],dp[j][k]+a[i].p);
				ans=max(ans,dp[i][j]);
			}
	}
	printf("%d\n",ans);
	return 0;
}
posted @ 2019-02-23 14:07  Starlight_Glimmer  阅读(7)  评论(0编辑  收藏  举报  来源
浏览器标题切换
浏览器标题切换end