Prometheus 釘釘告警模板
阿新 • • 發佈:2021-11-01
題目連結:
https://www.luogu.com.cn/problem/P5858
題目大意:
小 E 打算鑄劍,現在有 n 種原料,編號從 1 到 n,下標為 i 的原料堅固值為 ai, 按照 1 到 n 的順序依次加入鍊金爐,但是鍊金爐只能容納 w 個原料,每放入一個原料前可以取出不超過 s 個的原料,寶劍的耐久度為所有原料耐久度之和,而第 i 種原料的耐久度為放入這種原料時鍋內的原料總數(包括正在放入的)* ai,求最大的耐久度。
思路:
原料是按照順序放入的,同時放入的原料又有限制,可以想到構建一個二維 DP 陣列 dp[i][j], i 表示當前放入第幾個原料,j 表示當前爐中有幾個原料,於是我們可以寫出下面的程式碼。
for (LL i = 1; i <= n; i++) //當前是第幾個原料
for (LL j = w; j >= 1; j--) //當前爐中有幾個原料
for (LL k = j - 1; k <= min(w, j + s - 1); k++) //放入前可能剩下的原料
dp[i][j] = max(dp[i][j], dp[i - 1][k] + j * a[i]); //加入新的原料
上述程式碼的時間複雜度為 O(n * w * w),於是我們考慮優化,可以看到,最內層的 for 迴圈其實就是找到最大的耐久度後加入新原料,我們可以通過單調佇列來實現。
程式碼:
#include <bits/stdc++.h> using namespace std; #define LL long long const LL INF = -1e16 + 10; const int maxn = 5e3 + 10; LL n, w, s, dp[maxn][maxn], ans, q[maxn], pos[maxn]; vector <LL> a(maxn); int main(){ cin >> n >> w >> s; for (LL i = 1; i <= n; i++) scanf("%lld", &a[i]); for (LL i = 0; i <= n; i++) for (LL j = 0; j <= w; j++) dp[i][j] = INF; dp[0][0] = 0; for (LL i = 1; i <= n; i++){ LL l = 1, r = 1; q[l] = dp[i - 1][w]; pos[l] = w; for (LL j = w; j >= 1; j--){ while (l <= r && pos[l] > j + s - 1) l++; //取出原料 while (l <= r && q[r] < dp[i - 1][j - 1]) r--; //構建單調遞減的佇列 pos[++r] = j - 1; //記錄加入的原料的編號 q[r] = dp[i - 1][j - 1]; dp[i][j] = q[l] + j * a[i]; } } ans = INF; for (LL i = 0; i <= w; i++) ans = max(ans, dp[n][i]); cout << ans << "\n"; return 0; }