[NOIP 2024 模擬3]變幻

maniubi發表於2024-09-26

[NOIP 2024 模擬3]變幻

題意

給出長度為 \(n\) 的序列 \(a\)。可以進行 \(k\) 次修改。

每次修改可以把一個數變得更小。求序列中山谷數之和的最大值。

思路

動態規劃,定義 \(dp_{i,j}\) 表示前 \(i\) 個數進行 \(j\) 次修改的最大和。

因為連續兩個點只可能有一個成為山谷數,所以 \(i\)\(i-2\) 轉移得到。

轉移方程:

  1. 繼承 \(dp_{i,j}=dp_{i-1,j}\)
  2. 當前數為山谷數 \(dp_{i,j}=dp_{i-2,j}\)
  3. 當前數不為山谷數 \(dp_{i,j}=dp_{i-2,j-1}+\min \left \{a_{i-1},a_{i+1} \} \right.-1\)

\(i=2\) 轉移到 \(i=n-1\),因為 \(i=1\)\(i=n\) 沒用。

答案為 \(\min_{i=1}^{j} dp_{n-1,i}\)

程式碼

#include <bits/stdc++.h>
using namespace std;

using ll = long long;
const int N = 2e3 + 5;

int n, k, a[N];
ll dp[N][N], ans;

bool yes(int x) {
	if (x == 1 || x == n) {
		return 0;
	}
	return a[x] < a[x + 1] && a[x] < a[x - 1];
}

int main() {
	freopen("A.in", "r", stdin);
	freopen("A.out", "w", stdout);
	cin >> n >> k;
	for (int i = 1; i <= n; i ++) {
		cin >> a[i];
	}
	for (int i = 2; i < n; i ++) {
		for (int j = 0; j <= k; j ++) {
			dp[i][j] = dp[i - 1][j];
			if (yes(i)) {
				dp[i][j] = max(dp[i][j], dp[i - 2][j] + a[i]);
			}
			if (j) {
				dp[i][j] = max(dp[i][j], dp[i - 2][j - 1] + min({a[i - 1], a[i + 1], a[i]}) - 1);
			}
		}
	}
	for (int i = 0; i <= k; i ++) {
		ans = max(ans, dp[n - 1][i]);
	}
	cout << ans << "\n";
	return 0;
}