酷代码 AI
菜单
服务商
DeepSeek字节豆包
更多选项

单调队列优化DP:工人效率选择问题修正指南

看到你的代码和题目描述,我来帮你分析问题。 ## 🔍 问题分析 你的代码逻辑基本正确,但存在几个关键问题: ### ⚠️ 主要错误 **1. 单调队列维护的索引理解错误** 你的代码在维护 `dp[q[tt]] <= dp[i-1]` 时,但应该维护的是 `dp[q[tt]] - prefixSum[q[tt]]` 的单调性,因为DP转移方程是: ``` dp[i] = max(dp[i-1], dp[j] + prefixSum[i] - prefixSum[j]) = max(dp[i-1], prefixSum[i] + (dp[j] - prefixSum[j])) ``` 所以我们需要维护 `dp[j] - prefixSum[j]` 的单调递减(值越大越优)。 **2. 队列索引更新时机错误** 应该在计算 `dp[i]` 之前就加入 `i`(或者说正确的下标),而不是 `i-1`。 ## ✅ 修正后的代码 ```cpp #include <iostream> #include <vector> #include <algorithm> using namespace std; const int MAXN = 1e5 + 5; int n, k; long long efficiency[MAXN], prefixSum[MAXN], dp[MAXN]; int q[MAXN]; // 单调队列,存储索引 int main() { // 输入优化 ios::sync_with_stdio(false); cin.tie(nullptr); cin >> n >> k; for (int i = 1; i <= n; ++i) { cin >> efficiency[i]; prefixSum[i] = prefixSum[i - 1] + efficiency[i]; } int hh = 0, tt = -1; // 初始化:加入索引0,对应dp[0]=0, dp[0]-prefixSum[0]=0 q[++tt] = 0; for (int i = 1; i <= n; ++i) { // 移除超出窗口范围的元素 // 窗口范围:j 需要满足 i - j - 1 <= k,即 j >= i - k - 1 while (hh <= tt && q[hh] < i - k - 1) ++hh; // 更新 dp[i] // dp[i] = max(dp[i-1], dp[q[hh]] + prefixSum[i] - prefixSum[q[hh]]) dp[i] = max(dp[i - 1], dp[q[hh]] + prefixSum[i] - prefixSum[q[hh]]); // 维护单调队列:保持 dp[j] - prefixSum[j] 递减 // 注意:要插入的是索引 i,其对应的值是 dp[i] - prefixSum[i] while (hh <= tt && dp[q[tt]] - prefixSum[q[tt]] <= dp[i] - prefixSum[i]) { --tt; } q[++tt] = i; } cout << dp[n] << endl; return 0; } ``` ## 📝 关键修改点说明 ### 1️⃣ 单调队列维护的值 - **原来**:维护 `dp[q[tt]] <= dp[i-1]` - **修正**:维护 `dp[q[tt]] - prefixSum[q[tt]] <= dp[i] - prefixSum[i]` 因为状态转移方程可以转化为: ``` dp[i] = max(dp[i-1], prefixSum[i] + max(dp[j] - prefixSum[j])) ``` ### 2️⃣ 队列插入的时机和索引 - **原来**:在计算 `dp[i]` 后插入 `i-1` - **修正**:在计算 `dp[i]` 后插入 `i` 这是因为我们需要在新计算完 `dp[i]` 后,将其作为未来的候选状态加入队列。 ### 3️⃣ 代码优化建议 **a. 输入输出优化**: ```cpp ios::sync_with_stdio(false); cin.tie(nullptr); ``` 这是C++竞赛中的标准优化,可以大幅提升输入输出效率。 **b. 数据类型**: - `efficiency`、`prefixSum`、`dp` 都使用 `long long`,因为 `E_i ≤ 10^9`,`N ≤ 10^5`,最大和可能达到 `10^14`,超出 `int` 范围。 ### 4️⃣ 算法复杂度分析 | 操作 | 复杂度 | |------|--------| | 前缀和计算 | O(N) | | DP状态转移 | O(N) | | 单调队列维护 | 均摊O(N) | | **总时间复杂度** | **O(N)** | | **空间复杂度** | **O(N)** | ## 💡 算法思路 这是一个经典的**单调队列优化DP**问题: 1. **状态定义**:`dp[i]` 表示前i个工人能获得的最大效率 2. **状态转移**: ``` dp[i] = max(dp[i-1], dp[j] + sum(j+1, i)) ``` 其中 `j` 是一个有效的起点,使得 `j+1` 到 `i` 的连续工人数不超过 `k` 3. **优化核心**:通过单调队列维护候选的 `j`,将状态转移从 O(N) 降到 O(1) ## 🎯 测试验证 用样例测试: ``` 输入: 5 2 1 2 3 4 5 输出: 12 ``` 选择方案:选第1、2、4、5个工人(1+2+4+5=12),或者第1、3、5(1+3+5=9),最优为12。✓ ######[AI写代码神器 | 1249点数解答 | 2026-08-15 16:19:05]

相关提问
本站限时免费提问中
实用工具查看更多
Linux在线手册 [开发类]
Jquery在线手册 [开发类]
今日油价 [生活类]
图片互转base64 [开发类]
时间转换器 [开发类]