彈簧高蹺 [dp+改變列舉順序小優化]
阿新 • • 發佈:2019-01-03
文章目錄
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; }