P1525 关押罪犯【扩展域并查集】

题意:

题目链接

题解:

就是让最大的冲突最小。
我们从边权考虑,首先将边按边权从大到小排序,依次处理每一个边。我们尽量的使当前处理边权不出现,如果这个边权不可避免地一定会出现,那么最大值一定是当前处理的边权。

可以使用扩展域并查集来判断一个边是否是可以避免出现。
将一个点 x x 拆分成两个点, x 0 x_0 表示与 x x 在同一个监狱的犯人, x 1 x_1 表示不与 x x 在同一个监狱的犯人。
在判断一条边是否可以避免时,假设这条边端点为 x y x、y ,权值为 z z 。首先判断 x 0 x_0 y 0 y_0 是不是在一个集合,如果是则说明在避免前面的边的时候,已经将 x y x、y 放入一个集合中了,所以这条边无法避免,z就是答案。
否则,将 x 0 x_0 y 1 y_1 放入一个集合, x 1 x_1 y 0 y_0 放入一个集合。表示和x同一个监狱的人不和y同一个监狱的人是一个监狱,和y同一个监狱的人不和x同一个监狱的人 是一个监狱。保证 x y x、y 不是同一个监狱。

代码:

/**
* Author : Xiuchen
* Date : 2020-03-26-12.16.46
* Description : 关押罪犯
*/
#pragma GCC optimize(1)
#pragma GCC optimize(2)
#pragma GCC optimize(3,"Ofast","inline")
#include<cstdio>
#include<cstring>
#include<string>
#include<vector>
#include<stack>
#include<queue>
#include<map>
#include<cmath>
#include<math.h>
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long ll;
const int inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3fLL;
const int maxn = 2e4 + 100;
int gcd(int a, int b){
    return b ? gcd(b, a % b) : a;
}
int n, m;
int fa[maxn * 2];
struct node{
    int a, b, c;
} edge[100100];
bool cmp(node a, node b){
    return a.c > b.c;
}
int get(int x){
    if(x == fa[x]) return x;
    return fa[x] = get(fa[x]);
}
int main(){
    scanf("%d%d", &n, &m);
    for(int i = 1; i <= 2 * n; i++) fa[i] = i;
    for(int i = 1; i <= m; i++)
        scanf("%d%d%d", &edge[i].a, &edge[i].b, &edge[i].c);
    sort(edge + 1, edge + 1 + m, cmp);
    int ans = 0;
    for(int i = 1; i <= m; i++){
        int x_0 = edge[i].a, x_1 = edge[i].a + n;
        int y_0 = edge[i].b, y_1 = edge[i].b + n;
        if(get(x_0) == get(y_0) || get(x_1) == get(y_1)){
            ans = edge[i].c;
            break;
        }
        else{
            fa[get(x_1)] = get(y_0);
            fa[get(x_0)] = get(y_1);
        }
    }
    printf("%d\n", ans);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_44607936/article/details/105213213