当前位置:首页 > 比赛题解 > 2023年蓝桥杯省赛B组整数删除(洛谷P12085):优先队列+双向链表解法

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

6小时前比赛题解18

截图未命名.jpg 2023年蓝桥杯省赛B组整数删除(洛谷P12085):优先队列+双向链表解法 优先队列 双向链表 蓝桥杯真题 蓝桥杯 蓝桥杯省赛 C++ 洛谷题解 第1张

一、问题重述

给定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模拟最小堆实现高效取最小值

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

  2. 关键操作流程

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

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

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

  3. 复杂度分析

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

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

四、常见问题解答

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

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

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


原创内容 转载请注明出处

分享给朋友:

发表评论

访客

看不清,换一张

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