P1525 关押罪犯_并查集

P1525 关押罪犯 传送门

这道题是用并查集来写的, 但普通的并查集是做不出的. 所以需要用到一些巧妙地思维.

首先给边排序, 权值更大的在前面. 用贪心的思想(类似Kruskal算法)遍历边, 直到某一条边的两个顶点必须在同一个集合里面(一共只有两个集合).

这题的难点就是如何判断两个顶点一定在同一个集合. 所谓敌人的敌人就是朋友, 也就是说对于一个点, 如果它此前没有敌人, 那么遇到的第一个敌人就设为它的敌人, 此后, 再次遇见该点, 那么现在的敌人和以前的敌人就应该是同一阵营了(朋友), 通过这种规则进行合并, 从而实现两种阵营的划分.

那么, 得到答案的情况就是遍历到一条边时, 它的两个结点已经是同一阵营的了, 那么这条边的权值就是最小的最大冲突事件影响力.

需要注意的一种特殊情况是当可以做到没有冲突时, 应该输出0.

#include <algorithm>
#include <iostream>
#include <vector>
using namespace std;

struct Edge {
    int from, to, w;
};

bool cmp(Edge x, Edge y)
{
    return x.w > y.w;
}

const int maxn = 100000 + 5;
int n, m, parent[maxn], enemy[maxn] = {};
Edge edge[maxn];

int find(int x)
{
    return x == parent[x] ? x : parent[x] = find(parent[x]);
}

void add(int u, int v)
{
    u = find(u);
    v = find(v);
    if (u != v) parent[u] = v;
}

int main()
{
    cin >> n >> m;
    for (int i = 1; i <= n; ++i) parent[i] = i;
    for (int i = 0; i < m; ++i) {
        cin >> edge[i].from >> edge[i].to >> edge[i].w;
    }
    sort(edge, edge + m, cmp);
    for (int i = 0, u, v; i < m; ++i) {
        u = edge[i].from, v = edge[i].to;
        if (find(u) == find(v)) {
            cout << edge[i].w;
            return 0;
        } else {
            if (enemy[u]) add(enemy[u], v);
            else enemy[u] = v;
            if (enemy[v]) add(enemy[v], u);
            else enemy[v] = u;
        }
    }
    cout << 0;
}

猜你喜欢

转载自blog.csdn.net/wjh2622075127/article/details/81429411
今日推荐