1. 程式人生 > >P3089 [USACO13NOV]POGO的牛Pogo-Cow

P3089 [USACO13NOV]POGO的牛Pogo-Cow

P3089 [USACO13NOV]POGO的牛Pogo-Cow

FJ給奶牛貝西的腳安裝上了彈簧,使它可以在農場裡快速地跳躍,但是它還沒有學會如何降低速度。

FJ覺得讓貝西在一條直線的一維線路上進行練習,他在不同的目標點放置了N (1 <= N <= 1000)個目標點,目標點i在目標點x(i),該點得分為p(i)。貝西開始時可以選擇站在一個目標點上,只允許朝一個方向跳躍,從一目標點跳到另外一個目標點,每次跳躍的距離大於等於上一次跳躍的距離相等,並且必須跳到一個目標點。

每跳到一個目標點,貝西可以拿到該點的得分,請計算他的最大可能得分。

Solution

這題日了我好久啊
剛開始推了一下以為是單調佇列
後面發現有個點(沒打草稿腦算)推錯了。。

\(dp[i][j]\) 為跳到第 \(i\) 個點, 從 \(j\) 點跳過來的最大價值
那麼容易想到如下轉移: \[dp[i][j] = \max_{dis(i, j) \geq dis(j, k)}dp[j][k] + v[i]\]
其複雜度為 \(O(n^{3})\), 難以接受
考慮優化, 能不能省掉某一維列舉
當然從可行性入手, 觀察如下式子: \[dis(i, j) \geq dis(j, k)\]
對於一個特定的 \(j\) , 我們將其dp陣列全部列出 \[dp[j][1], dp[j][2], dp[j][3],...,dp[j][j]\]
排除 \(i\) 的影響, 我們先把式子寫成 \(dis(j, k) \leq t\)

\[abs(p_{j} - p_{k}) \leq t\]
\(i, j\) 不變, 觀察 \(k\) 對不等式成立的影響, 發現當 \(k\) 越小, 不等式越有可能成立
也就是說, 存在一個位置 \(x\) , 滿足:\[abs(p_{j} - p_{x}) \leq t\] \[abs(p_{j} - p_{x - 1}) > t\]
\([x, j]\) 範圍內 \(dp[j][k]\) 全部可取, \([1, x - 1]\) 範圍內 \(dp[j][k]\) 全部不可取
對於特定的 \(j\)\(t\) 隨著 \(i\) 的增大而增大, 所以 \(x\)\(i\)
的增大而減小
於是對於每個 \(j\) 維護一個位置 \(x\), 記為 \(head[j]\) 表示其右端全部可取
同時維護可取的部分的最大值即可

Code

#include<iostream>
#include<cstdio>
#include<queue>
#include<cstring>
#include<algorithm>
#include<climits>
#define LL long long
#define REP(i, x, y) for(int i = (x);i <= (y);i++)
using namespace std;
int RD(){
    int out = 0,flag = 1;char c = getchar();
    while(c < '0' || c >'9'){if(c == '-')flag = -1;c = getchar();}
    while(c >= '0' && c <= '9'){out = out * 10 + c - '0';c = getchar();}
    return flag * out;
    }
const int maxn = 2019;
int num, ans;
struct Point{int p, v;}p[maxn];
int dp[maxn][maxn];//跳到i,從j跳過來的最大得分
bool cmp1(Point a, Point b){return a.p < b.p;}
bool cmp2(Point a, Point b){return a.p > b.p;}
void init(){
    num = RD();
    REP(i, 1, num)p[i] = (Point){RD(), RD()};
    }
int head[maxn];//目前合法的第一個dp[i][x]位置
int maxx[maxn];//目前最大的關於i的上一項
void DP(){
    memset(dp, 0, sizeof(dp));
    REP(i, 1, num)dp[i][i] = maxx[i] = p[i].v, head[i] = i;//作為起點
    REP(i, 1, num){
        REP(j, 1, i - 1){
            dp[i][j] = maxx[j] + p[i].v;
            ans = max(ans, dp[i][j]);
            }
        REP(j, 1, i){
            while(head[j] > 1 && (abs(p[i + 1].p - p[j].p) >= abs(p[j].p - p[head[j] - 1].p))){
                head[j]--;
                maxx[j] = max(maxx[j], dp[j][head[j]]);
                }
            }
        }
    }
void solve(){
    sort(p + 1, p + 1 + num, cmp1);//順著排序
    DP();
    sort(p + 1, p + 1 + num, cmp2);//倒著排序
    DP();
    printf("%d\n", ans);
    }
int main(){
    init();
    solve();
    return 0;
    }