P1878 舞蹈课 结构体优先队列,重载运算符

解题思路

本题需要模拟舞蹈课上学生配对跳舞的过程,关键点在于:

  1. 异性相邻配对:只有相邻的男生('B')和女生('G')才能配对

  2. 最小差值优先:优先选择舞蹈技术差值最小的一对

  3. 左边优先:当有多对差值相同时,选择最左边的一对

  4. 动态更新:每次配对后,队伍会合并,需要动态维护新的相邻关系

算法选择

使用优先队列+双向链表的解决方案:

  • 优先队列:用于快速获取当前差值最小的相邻对

  • 双向链表:维护队伍中学生的左右邻居关系,方便动态更新

代码注释

#include<bits/stdc++.h>
#define pii pair<int,int>
#define ll long long
using namespace std;
const int N = 2e5 + 10;

int n;
string s;
ll a[N], vis[N], left_node[N], right_node[N]; // a存储舞蹈技术值,vis标记是否已配对
pii ans[N]; // 存储配对结果
int top; // 记录配对数

// 自定义结构体,存储配对信息
struct Pair {
    ll diff; // 舞蹈技术差值
    ll x, y; // 配对的两个学生编号
    
    // 重载小于运算符,用于优先队列排序
    bool operator<(const Pair& other) const {
        if(diff != other.diff) 
            return diff > other.diff; // 差值小的优先
        return x > other.x; // 差值相同时,编号小的优先
    }
};

priority_queue<Pair> q; // 优先队列,存储所有可能的配对

int main() {
    ios::sync_with_stdio(false);
    cin.tie(0);
    
    // 输入处理
    cin >> n >> s;
    for(int i = 1; i <= n; i++) cin >> a[i];
    s = '.' + s; // 让字符串下标从1开始
    
    // 初始化双向链表
    for(int i = 1; i <= n; i++) {
        left_node[i] = i - 1; // 左邻居
        right_node[i] = i + 1; // 右邻居
    }
    right_node[n] = 0; // 末尾没有右邻居
    
    // 初始化优先队列,找出所有初始相邻异性对
    for(int i = 2; i <= n; i++) {
        if(s[i] != s[i-1]) { // 如果是异性相邻
            q.push({abs(a[i] - a[i-1]), i-1, i}); // 计算差值并加入队列
        }
    }
    
    // 处理配对过程
    while(!q.empty()) {
        auto current = q.top();
        q.pop();
        int x = current.x, y = current.y;
        
        // 如果任一学生已经配对过,跳过
        if(vis[x] || vis[y]) continue;
        
        // 记录配对结果
        vis[x] = vis[y] = 1;
        ans[++top] = {min(x,y), max(x,y)};
        
        // 更新双向链表
        int l = left_node[x], r = right_node[y];
        if(l) right_node[l] = r; // x的左邻居指向y的右邻居
        if(r) left_node[r] = l; // y的右邻居指向x的左邻居
        
        // 检查新形成的相邻对是否可以配对
        if(l && r && s[l] != s[r] && !vis[l] && !vis[r]) {
            q.push({abs(a[l] - a[r]), l, r});
        }
    }
    
    // 输出结果
    cout << top << endl;
    for(int i = 1; i <= top; i++) {
        cout << ans[i].first << " " << ans[i].second << endl;
    }
    
    return 0;
}

关于运算符重载的详细说明

在结构体Pair中重载了operator<运算符,这是为了定义优先队列中的排序规则:

bool operator<(const Pair& other) const {
    if(diff != other.diff) 
        return diff > other.diff; // 差值小的优先
    return x > other.x; // 差值相同时,编号小的优先
}

这里有几个关键点需要注意:

  1. 默认优先队列是最大堆,即默认使用less比较,较大的元素优先级高

  2. 我们想要的是差值最小的优先,所以当diff > other.diff时返回true,这样较小的diff会被认为"更小"(优先级更高)

  3. 当差值相同时,我们想要编号较小的优先,所以当x > other.x时返回true,这样较小的x会被认为"更小"

这种设计确保了优先队列总是能返回:

  1. 舞蹈技术差值最小的对

  2. 当差值相同时,最左边的对(即编号较小的对)

示例解析

以题目中的输入为例:

4
BGBG
4 2 4 3

初始相邻异性对:

  • (1,2): |4-2|=2

  • (2,3): |2-4|=2

  • (3,4): |4-3|=1

处理过程:

  1. 取出(3,4)配对,队伍变为B B G

  2. 新相邻对(2,3)但都是B,不配对

  3. 取出(1,2)配对

最终输出:

2
3 4
1 2

 

posted @ 2025-05-30 17:57  CRt0729  阅读(27)  评论(0)    收藏  举报