当前位置:首页 > 洛谷 > 洛谷P3800题解:动态规划与单调队列优化的高效解法

洛谷P3800题解:动态规划与单调队列优化的高效解法

3个月前 (08-14)

洛谷P3800题解:动态规划与单调队列优化的高效解法 洛谷题解 动态规划 单调队列 滑动窗口算法 C++ 第1张

一、题目解读

洛谷P3800题要求在一个N×M的网格中,每个点有能量值,从第一行出发到第N行,每次可向左/右移动不超过T个位置,求路径上能量值总和的最大值。题目核心在于如何高效处理路径限制与能量累积,考验动态规划与优化技巧的应用。

二、解题思路

参考代码采用动态规划+单调队列优化。定义dp[i][j]为从起点到(i,j)位置的最大能量和,利用单调队列维护每一行中“可到达列”的递减能量值序列,确保滑动窗口内始终取到最优前缀。通过双向处理(正向+反向维护队列),兼顾左右移动限制,降低时间复杂度至O(NM),突破暴力枚举的瓶颈。

三、解题步骤

1. 数据读取与初始化:存储K个特殊点的坐标与能量值,初始化第一行dp值(即直接取网格能量)。

2. 动态规划核心逻辑:

○ 正向处理:维护列j的单调递减队列,确保队首为当前可覆盖范围内(j-T,j)的最大dp值,更新dp[i][j] = 队首值 + 当前网格能量。

○ 反向处理:同理维护队列,覆盖范围变为(j,j+T),取双向计算结果中的最大值。

3. 结果输出:遍历最后一行dp值,输出最大值。

四、代码与注释

#include <iostream>
#include <vector>
#include <deque>
#include <algorithm>
using namespace std;

typedef long long ll;
const int MAX_N = 5005;
const int MAX_M = 5005;

struct Point {
    int x, y, v;
};

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    
    int N, M, K, T;
    cin >> N >> M >> K >> T;
    
    vector<vector<ll>> grid(N+1, vector<ll>(M+1, 0));
    vector<vector<ll>> dp(N+1, vector<ll>(M+1, 0));
    
    // 读取P点数据
    for (int i = 0; i < K; ++i) {
        int x, y, v;
        cin >> x >> y >> v;
        grid[x][y] = v;
    }
    
    // 初始化第一行
    for (int j = 1; j <= M; ++j) {
        dp[1][j] = grid[1][j];
    }
    
    // 动态规划处理每一行
    for (int i = 2; i <= N; ++i) {
        deque<int> dq;
        
        // 处理每一列,使用单调队列优化
        for (int j = 1; j <= M; ++j) {
            // 维护单调队列,保持队列中dp[i-1][k]递减
            while (!dq.empty() && dp[i-1][dq.back()] <= dp[i-1][j]) {
                dq.pop_back();
            }
            dq.push_back(j);
            
            // 移除超出T范围的列
            while (!dq.empty() && dq.front() < j - T) {
                dq.pop_front();
            }
            
            // 计算当前列的最大值
            dp[i][j] = dp[i-1][dq.front()] + grid[i][j];
        }
        
        // 反向处理,考虑从右边移动过来的情况
        dq.clear();
        for (int j = M; j >= 1; --j) {
            while (!dq.empty() && dp[i-1][dq.back()] <= dp[i-1][j]) {
                dq.pop_back();
            }
            dq.push_back(j);
            
            while (!dq.empty() && dq.front() > j + T) {
                dq.pop_front();
            }
            
            // 取两个方向的最大值
            dp[i][j] = max(dp[i][j], dp[i-1][dq.front()] + grid[i][j]);
        }
    }
    
    // 输出最后一行中的最大值
    ll max_power = *max_element(dp[N].begin(), dp[N].end());
    cout << max_power << endl;
    
    return 0;
}

五、总结

本题通过动态规划构建状态转移方程,结合单调队列优化,巧妙将二维路径问题转化为一维滑动窗口求解。关键在于双向队列维护的边界判断,既保证了路径限制的合法性,又通过单调性避免了冗余计算。该解法为处理带区间限制的优化问题提供了经典范式。

原创内容 转载请注明出处

分享给朋友:

相关文章

力扣第1991题:寻找数组的中心索引 如何找到左右和相等的中心索引

力扣第1991题:寻找数组的中心索引 如何找到左右和相等的中心索引

题目解读给定一个整数数组,我们需要找到一个中心索引,使得该索引左侧所有元素的和等于右侧所有元素的和。如果不存在这样的索引,则返回-1。中心索引的定义不包含在左右两侧的和计算中。这个问题考察对数组遍历和...

从零到一掌握背包问题:洛谷P1164题解精讲,附带优化

从零到一掌握背包问题:洛谷P1164题解精讲,附带优化

题目重解:小A带着m元钱来到餐馆,菜单上有n道菜,每道菜都有确定的价格。现在需要计算出刚好花完m元的点菜方案总数。这个问题看似简单,但当菜品数量增多时,暴力枚举就会变得不可行,需要更高效的算法来解决。...

CSP-J 2019纪念品题解(洛谷P5662):动态规划+完全背包问题的实战应用

CSP-J 2019纪念品题解(洛谷P5662):动态规划+完全背包问题的实战应用

一、题目解读2019年CSP-J的“纪念品”问题(对应洛谷P5662)要求玩家在T天内通过买卖纪念品最大化金币收益。每天可交易N种商品,需计算最优策略下的最终金币数。题目强调动态规划思维与资源分配优化...

NOIP 2008火柴棒等式题解(C++代码实现)  动态规划与枚举算法详解

NOIP 2008火柴棒等式题解(C++代码实现) 动态规划与枚举算法详解

一、题目解读火柴棒等式问题(NOIP 2008,洛谷P1149)要求使用给定数量的火柴棒,构造形如 A + B = C 的等式,其中A、B、C均为整数,且火柴棒总数恰好等于输入值。需统计符合条件的等式...

洛谷P4551题解题报告:图论与Trie树优化异或路径问题的实战解析

洛谷P4551题解题报告:图论与Trie树优化异或路径问题的实战解析

一、题目解读洛谷P4551题要求在一个无向图中,寻找任意两点路径权值异或后的最大值。题目输入为图的边信息(点数n和n-1条边),每条边包含起点、终点及权值。需输出所有路径中权值异或的最大值。问题核心在...

CSP-J 2019公交换乘题解析:基于队列优化的动态规划代码详解

CSP-J 2019公交换乘题解析:基于队列优化的动态规划代码详解

一、题目解读CSP-J 2019年的“公交换乘”题目(洛谷P5661)要求模拟地铁与公交交替出行的费用计算。题目核心在于地铁消费会产生优惠券,而公交可在45分钟内使用优惠券抵扣车费。需要处理n条出行记...

发表评论

访客

看不清,换一张

◎欢迎参与讨论,请在这里发表您的看法和观点。