P1725 【琪露諾】dp-單調隊列優化

題目鏈接:https://www.luogu.org/problemnew/show/P1725

看了題後不難想到這個題是個dp,而狀態轉移方程爲dp[i] = max(dp[k]) (k >=i+l && k <= i+r) + a[i]。如果暴力去dp會多了一層(l,r)的循環,可能會超時,所以對區間(l,r)進行優化,用單調隊列進行優化。維護區間【l,r】的最大值即可。

#include <bits/stdc++.h>
typedef long long ll;
const int INF = 0x3f3f3f3f;
const int MAXN = 2e6+7;
using namespace std;
int a[MAXN];
int dp[MAXN],q[MAXN]; //單調隊列存的是下標

int main() {
    int n,l,r;
    cin >> n >> l >> r;
    for(int i = 0;i <= n;i++)
        cin >> a[i];

    int head = 1,tail = 0,p = 0;
    int ans = -1;
    memset(dp,-1,sizeof(dp)); //對於所有的點初始值都爲最小
    dp[0] = 0;//在0點爲0
    for(int i = l;i <= n;i++){  //從0開始,第一個能跳的點就是l,往後依次判斷
        while(head <= tail && dp[q[tail]] < dp[p])
            tail--; //如果當前隊列內的值小於最新的值,直接把他刪去即可
        tail++;
        q[tail] = p;//記錄是第幾個入隊的
        if(q[head] + r < i)  //如果此時隊頭的下標加上r比i小,要把隊頭刪去
            head++;
        dp[i] = dp[q[head]] + a[i];
        p++;
    }
    for(int i = n-r+1;i <= n;i++)  //對於能跳到對岸的點找一個最大值即可
        ans = max(ans,dp[i]);
    cout << ans << endl;
    return 0;
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章