当前位置:首页 > 比赛题解 > 动态规划经典应用:2022年CSP-J上升点列问题详解与代码实现

动态规划经典应用:2022年CSP-J上升点列问题详解与代码实现

6小时前比赛题解17

截图未命名.jpg 动态规划经典应用:2022年CSP-J上升点列问题详解与代码实现 动态规划 CSP-J竞赛 上升点列 算法解析 二维排序 第1张

一、问题理解与算法思路

题目要求在一组二维坐标点中,找出最长的严格递增序列(x和y都严格递增),允许插入最多k个额外点来连接不相邻的点。这个问题可以抽象为在二维平面上的路径规划问题,需要找到最优的连接方式。

二、完整代码实现(带详细注释)

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

// 定义二维点结构体
struct Point {
    int x, y;
    // 重载比较运算符,用于排序
    bool operator<(const Point& other) const {
        return x < other.x || (x == other.x && y < other.y);
    }
};

int main() {
    // 输入输出优化
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    
    int n, k;
    cin >> n >> k;  // 输入点数和可添加点数
    
    vector<Point> points(n);
    for (int i = 0; i < n; ++i) {
        cin >> points[i].x >> points[i].y;  // 输入每个点的坐标
    }
    
    // 对点集进行排序:先按x升序,x相同则按y升序
    sort(points.begin(), points.end());
    
    // 动态规划表初始化
    // dp[i][j]表示以第i个点结尾,使用j个额外点时的最长序列长度
    vector<vector<int>> dp(n, vector<int>(k + 1, 1));
    int max_len = 1;  // 记录全局最大长度
    
    // 动态规划主循环
    for (int i = 0; i < n; ++i) {  // 遍历每个点作为终点
        for (int j = 0; j <= k; ++j) {  // 遍历可能的额外点数
            dp[i][j] = 1;  // 初始化为只包含当前点
            
            for (int prev = 0; prev < i; ++prev) {  // 检查所有可能的前驱点
                // 确保前驱点在当前点左下方
                if (points[prev].x > points[i].x || points[prev].y > points[i].y) {
                    continue;
                }
                
                // 计算两个点之间需要插入的点数
                int dx = points[i].x - points[prev].x;
                int dy = points[i].y - points[prev].y;
                int needed = dx + dy - 1;  // 需要插入的点数
                
                // 如果当前可用点数足够
                if (needed <= j) {
                    // 状态转移方程
                    dp[i][j] = max(dp[i][j], dp[prev][j - needed] + dx + dy);
                }
            }
            
            // 考虑使用剩余的k-j个点来延长序列
            max_len = max(max_len, dp[i][j] + (k - j));
        }
    }
    
    cout << max_len << endl;  // 输出结果
    return 0;
}

三、算法核心解析

  1. 预处理排序:将点按x坐标升序排列,x相同时按y升序排列,确保后续处理的有序性。

  2. 动态规划状态定义

    • dp[i][j]:表示以第i个点结尾,使用j个额外点时的最长序列长度

    • 初始状态:每个点单独构成序列,长度为1

  3. 状态转移过程

    • 对于每个点i,检查所有可能的前驱点prev

    • 确保prev点在i点的左下方(x和y都更小)

    • 计算两点间需要插入的点数needed = dx + dy - 1

    • 如果可用点数j >= needed,则更新dp[i][j]

  4. 全局最大值更新

    • 考虑使用剩余的k-j个点来延长当前序列

    • 实时更新max_len记录全局最大值

四、复杂度分析与优化

  • 时间复杂度:O(n²k)

  • 空间复杂度:O(nk)

  • 可能的优化方向:

五、学习要点总结

  1. 理解动态规划在多维状态下的应用

  2. 掌握二维问题的状态定义技巧

  3. 学会处理带约束条件的动态规划问题

  4. 理解排序预处理在算法中的重要性

参考:动态规划

原创内容 转载请注明出处

分享给朋友:

发表评论

访客

看不清,换一张

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