为了账号安全,请及时绑定邮箱和手机立即绑定

2023年蓝桥杯省赛B组整数删除(洛谷P12085):优先队列+双向链表解法

标签:
C++

https://img1.sycdn.imooc.com/016c8768087a089e09060653.jpg

一、问题重述

给定N个整数的序列,进行K次操作:每次删除当前最小元素,并将其值加到相邻元素上。要求高效计算出最终序列。

二、完整代码解析(含详细注释)

#include <iostream>#include <vector>#include <queue>#include <set>using namespace std;// 定义链表节点结构struct Node {
    long long value;   // 存储节点值
    int prev, next;    // 前驱和后继指针
    // 重载运算符用于set排序
    bool operator<(const Node& other) const {
        return value < other.value || (value == other.value && prev < other.prev);
    }};int main() {
    ios::sync_with_stdio(false);  // 加速输入输出
    cin.tie(nullptr);
    
    int N, K;
    cin >> N >> K;  // 输入数字个数和操作次数
    
    // 初始化双向链表(含哨兵节点)
    vector<Node> nodes(N+2);  
    set<pair<long long, int>> min_heap;  // 使用set模拟最小堆
    
    // 构建初始链表和优先队列
    for (int i = 1; i <= N; ++i) {
        cin >> nodes[i].value;
        nodes[i].prev = i-1;
        nodes[i].next = i+1;
        min_heap.insert({nodes[i].value, i});  // 存入值和位置
    }
    // 设置哨兵节点
    nodes[0].next = 1;     // 头哨兵
    nodes[N+1].prev = N;   // 尾哨兵
    
    vector<bool> deleted(N+2, false);  // 标记删除状态
    
    // 执行K次删除操作
    while (K--) {
        auto it = min_heap.begin();  // 获取最小值
        long long val = it->first;
        int pos = it->second;
        min_heap.erase(it);
        
        // 处理前驱节点
        int prev_pos = nodes[pos].prev;
        if (prev_pos != 0) {  // 如果不是头节点
            min_heap.erase({nodes[prev_pos].value, prev_pos});
            nodes[prev_pos].value += val;  // 前驱节点增值
            min_heap.insert({nodes[prev_pos].value, prev_pos});
        }
        
        // 处理后继节点
        int next_pos = nodes[pos].next;
        if (next_pos != N+1) {  // 如果不是尾节点
            min_heap.erase({nodes[next_pos].value, next_pos});
            nodes[next_pos].value += val;  // 后继节点增值
            min_heap.insert({nodes[next_pos].value, next_pos});
        }
        
        // 更新链表指针
        nodes[prev_pos].next = next_pos;
        nodes[next_pos].prev = prev_pos;
        deleted[pos] = true;  // 标记删除
    }
    
    // 输出最终序列
    int current = nodes[0].next;  // 从第一个有效节点开始
    while (current != N+1) {
        cout << nodes[current].value << " ";
        current = nodes[current].next;
    }
    
    return 0;}

三、核心算法解析

  1. 数据结构设计

  • 双向链表维护元素间关系

  • set模拟最小堆实现高效取最小值

  • 哨兵节点简化边界条件处理

关键操作流程

  • 每次取出最小值后更新相邻节点

  • 动态维护优先队列中的值

  • 通过标记数组避免重复处理

复杂度分析

  • 时间复杂度:O(KlogN)(每次堆操作logN)

  • 空间复杂度:O(N)(存储链表和堆)

四、常见问题解答

Q:为什么用set而不用priority_queue? A:set支持快速查找和删除任意元素,priority_queue只能访问堆顶

Q:如何处理相同最小值的冲突? A:通过自定义比较函数,当值相同时按位置排序

Q:哨兵节点有什么作用? A:统一处理头尾节点的边界情况,避免额外条件判断

来源:编程学习


点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
  • 推荐
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
微信客服

购课补贴
联系客服咨询优惠详情

帮助反馈 APP下载

慕课网APP
您的移动学习伙伴

公众号

扫描二维码
关注慕课网微信公众号

举报

0/150
提交
取消