Loading

AT ABC285E Work or Rest 题解

Link

有趣的 DP 题,难点在于从哪里开始入手以及优化(也许)。

显然 DP 可以方便地处理这个 \(\max\) 值的转移,但是从哪个位置开始 DP 呢?注意到周期呈现环状,也就是说一周的第 \(n\) 天和下一周的第 \(1\) 天是可以结合产生贡献的,我们钦定第 \(1\) 天休息开始 DP,答案最终存储在 \(dp_{n + 1}\) 中。令 \(dp_i\) 表示在前 \(i\) 天中,最近一次休息在第 \(i\) 天时的最大价值,有转移:

\[dp_{r} = \max_{l = 1}^{r} \{ dp_l + v(l, r) \} \]

其中 \(v(l, r)\) 表示第 \(l\) 天和第 \(r\) 天休息,在 \([l + 1, r - 1]\) 连续工作时的价值获取总和。注意到这个东西呈现出一个“单峰”的趋势,从中间断开形成一个镜像形状形如 \(a_1, a_2, a_3, \dots, a_3, a_2, a_1\),尝试通过前缀和来维护这个东西,将区间长度除以 \(2\) 之后分奇偶算贡献。

#include <bits/stdc++.h>

using i64 = long long;

constexpr int N = 5007;

int n;
i64 a[N], s[N], dp[N];

i64 calc(int i) {
	return (s[i / 2] * 2 + (i & 1 ? a[i / 2 + 1] : 0));
}

int main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);

    std::cin >> n;
    for (int i = 1; i <= n; i++) {
    	std::cin >> a[i];
    	s[i] = s[i - 1] + a[i];
	}
	dp[1] = calc(n - 1);
	for (int i = 2; i <= n; i++) {
		for (int j = 1; j < i; j++)
			dp[i] = std::max(dp[i], dp[j] - calc(n - j) + calc(i - j - 1) + calc(n - i));
	}
	std::cout << *std::max_element(dp + 1, dp + n + 1) << "\n";
	return 0;
}
posted @ 2025-11-02 21:42  夢回路  阅读(5)  评论(0)    收藏  举报