P1525 [NOIP 2010 提高组] 关押罪犯

种类并查集的核心思想

种类并查集通过扩展原始并查集的域来表示对象之间的多种关系(如敌对、友好等)。在本题中:

  • 原始域(1~n):表示罪犯在监狱A

  • 扩展域(n+1~2n):表示同一个罪犯在监狱B的"虚拟镜像"

关键算法说明:

  1. 贪心策略:将冲突按怨气值从大到小排序,优先处理怨气值大的冲突对。

  2. 并查集扩展域

    • 使用1~n表示罪犯在监狱A

    • 使用n+1~2n表示罪犯在监狱B

    • 当处理(x,y)时:

      • merge(x, y+n)表示如果x在A,则y必须在B

      • merge(y, x+n)表示如果y在A,则x必须在B

  3. 冲突检测:如果在处理某对(x,y)时发现find(x)==find(y),说明按照之前的分法,这两个罪犯已经被分到同一监狱,此时当前怨气值就是答案。

  4. 时间复杂度:O(Mα(N)),其中α是反阿克曼函数,可以认为是常数。

#include<bits/stdc++.h>
using namespace std;

const int N = 2e5 + 10;  // 数组大小设为2倍罪犯数量(N≤20000)
const int inf = 0x3f3f3f3f;  // 定义一个无穷大常量(虽然本题未使用)

struct node {
    int x, y, z;  // 存储罪犯对:x和y是罪犯编号,z是怨气值
};

node t[N];  // 存储所有罪犯关系
int n, m;   // n-罪犯数量,m-关系数量
int f[N];   // 并查集父节点数组

// 比较函数:按怨气值从大到小排序
bool cmp(node a, node b) {
    return a.z > b.z;
}

// 并查集查找函数(带路径压缩)
int find(int x) {
    if(f[x] != x) 
        f[x] = find(f[x]);  // 路径压缩优化
    return f[x];
}

// 并查集合并函数
void merge(int x, int y) {
    int fx = find(x), fy = find(y);
    f[fy] = fx;  // 将fy的父节点设为fx
}

int main() {
    cin >> n >> m;
    
    // 输入所有罪犯关系
    for(int i = 1; i <= m; i++) 
        cin >> t[i].x >> t[i].y >> t[i].z;
    
    // 按怨气值从大到小排序(贪心处理)
    sort(t + 1, t + 1 + m, cmp);
    
    // 初始化并查集:1~n表示监狱A,n+1~2n表示监狱B
    for(int i = 1; i <= 2 * n; i++) 
        f[i] = i;
    
    // 处理每条关系(从怨气值大的开始处理)
    for(int i = 1; i <= m; i++) {
        int x = t[i].x, y = t[i].y;
        
        // 如果x和y已经在同一集合(即被分到同一监狱)
        if(find(x) == find(y)) {
            cout << t[i].z;  // 直接输出当前怨气值(因为是排序后第一个冲突)
            return 0;
        }
        else {
            // 将x和y分到不同监狱:
            // x在A则y在B,x在B则y在A
            merge(x, y + n);  // x和y的"敌人"(y+n)合并
            merge(y, x + n);  // y和x的"敌人"(x+n)合并
        }
    }
    
    // 如果没有冲突发生
    cout << 0;
    return 0;
}

 

posted @ 2025-04-23 18:40  CRt0729  阅读(7)  评论(0)    收藏  举报