1. 程式人生 > 其它 >Prometheus 釘釘告警模板

Prometheus 釘釘告警模板

題目連結:

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;
}