原題連結:https://www.luogu.com.cn/problem/P1525
題意解讀:有很多罪犯,要關到兩座監獄,有一些罪犯之間有仇,並且可以量化出仇恨值,如果關在一起就會衝突,造成的影響就是仇恨值,要使得造成的影響最小,如果可以完全不起衝突,輸出0。
解題思路:
首先,要讓衝突影響最小化,顯然應該把仇恨大的罪犯分開。
將所有罪犯關係、仇恨值按仇恨值大小降序排序
遍歷每一對罪犯,判斷他們是否已經在同一個集合(監獄)
如果已經屬於同一個集合,則輸出他們的仇恨值,即為答案。
如果不屬於同一個集合,就要將兩人分到兩個集合(監獄),問題的關鍵來了,如何分配罪犯?
初始時,如果兩人之前都沒有仇人,則不著急合併,記錄下兩人為各自的仇人
接下來,如果兩人能找到各自仇人,則將其與對方的仇人放在一個集合。(解釋:a、b兩人,a如果已經有仇人了,說明a和仇人的仇恨值更大,因為是在前面遍歷到,a顯然不能和其仇人分到一個集合,應該將b跟a的仇人分到一個集合,同樣,a應該跟b的仇人分到一個集合)
100分程式碼:
#include <bits/stdc++.h>
using namespace std;
const int N = 20005, M = 100005;
int p[N]; //並查集,罪犯所屬的集合
//查詢x所在集合
int find(int x)
{
if(p[x] == x) return p[x];
return p[x] = find(p[x]);
}
//將x、y合併
void merge(int x, int y)
{
p[find(x)] = find(y);
}
struct node
{
int a, b, c; //a 和 b的怨氣值c
} s[M];
bool cmp(node x, node y)
{
return x.c >= y.c;
}
int enemy[N]; //儲存已出現的每個人的敵人-存在仇恨值的人
int n, m;
int main()
{
cin >> n >> m;
for(int i = 1; i <= m; i++)
{
cin >> s[i].a >> s[i].b >> s[i].c;
}
for(int i = 1; i <= n; i++) p[i] = i;
sort(s + 1, s + m + 1, cmp); //按仇恨值降序排序
bool nowar = true; //是否沒有任何衝突
for(int i = 1; i <= m; i++)
{
int u = s[i].a, v = s[i].b;
if(find(u) == find(v)) //如果兩個人已經屬於同一個集合,則無法再劃分,此時的仇恨值即答案
{
cout << s[i].c;
nowar = false; //說明會產生衝突
break;
}
else
{
if(!enemy[u]) enemy[u] = v; //如果u沒有敵人,給u設定敵人v
else merge(enemy[u], v); //如果u有敵人,把v和u的敵人分到一個集合
if(!enemy[v]) enemy[v] = u; //如果v沒有敵人,給v設定敵人u
else merge(enemy[v], u); //如果v有敵人,把u和v的敵人分到一個集合
}
}
if(nowar) cout << 0; //如果沒有任何衝突,輸出0
return 0;
}