思路
動態規劃,單調佇列
動態規劃
觀察題目,發現下標為 \(i\) 的點只能對 \([i + l, i + r]\) 區間的點產生貢獻。
設 \(f_i\) 為到達點 \(i\) 時的最大凍結指數。
易得狀態轉移方程式:\(f_k \leftarrow \max(f_k, f_i+a_k),(k \in [i + l, i + r])\)。
若直接對該方程進行轉移,時間複雜度是 \(O(n^2)\) 的,會爆。雖然資料水放過去了。
單調佇列
考慮最佳化,對題目給出的條件進行轉化,發現能對 \(i\) 產生貢獻的,只有 \([i - r, i - l]\) 區間的點。
又因為 \(f_i\) 為到達點 \(i\) 時的最大凍結指數,所以產生貢獻的點時明確的,即 \([i - r, i - l]\) 區間中最大值,這恰好又是一個滑動視窗,自然能用單調佇列最佳化。
細節
-
在一些資料中,有一些點是無法到達的,比如這個:
5 3 4 0 1 2 3 4 5
下標為 \(5\) 的點是無法到達的,而如果維護單調佇列時 \(f_1\) 為 \(0\) 的話,\(f_5\) 會為 \(5\)。
所以應該把 \(f\) 陣列初始化為負無窮的。 -
最後統計答案時只要統計 \([n + 1 - r, n]\) 的區間內的即可。
-
統計答案時要預先把答案製成負無窮。
程式碼
點選檢視程式碼
/*
--------------------------------
| code by FRZ_29 |
| code time |
| 2024/08/10 |
| 08:16:15 |
| 星期六 |
--------------------------------
*/
#include <iostream>
#include <cstdio>
#include <deque>
using namespace std;
void RD() {}
template<typename T, typename... U> void RD(T &x, U&... arg) {
x = 0; int f = 1;
char ch = getchar();
while (ch < '0' || ch > '9') { if (ch == '-') f = -1; ch = getchar(); }
while (ch >= '0' && ch <= '9') x = (x << 3) + (x << 1) + ch - '0', ch = getchar();
x *= f; RD(arg...);
}
const int N = 2e5 + 5, inf = -2e9;
#define PB push_back
#define PPB pop_back
#define PPF pop_front
#define LF(i, __l, __r) for (int i = __l; i <= __r; i++)
#define RF(i, __r, __l) for (int i = __r; i >= __l; i--)
int n, l, r, ans = inf;
int f[N], a[N];
deque<int> deq;
int main() {
RD(n, l, r);
LF(i, 0, n) RD(a[i]);
deq.PB(0);
LF(i, 1, n) f[i] = inf;
LF(i, l, n) {
while (deq.size() && i - r > deq.front()) deq.PPF();
// printf("deq.front = %d\n", deq.front());
f[i] = f[deq.front()] + a[i];
while (deq.size() && f[i - l + 1] > f[deq.back()]) deq.PPB();
deq.PB(i - l + 1);
// printf("f[%d] = %d\n", i, f[i]);
}
// LF(i, 0, n) printf("f[%d] = %d ", i, f[i]);
// puts("");
LF(i, n + 1 - r, n) ans = max(ans, f[i]);
printf("%d\n", ans);
return 0;
}
/* ps:FRZ弱爆了,10幾天有思路今天才寫 */