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;
}