弹簧高跷 [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;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值