1. 程式人生 > >彈簧高蹺 [dp+改變列舉順序小優化]

彈簧高蹺 [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;
}